In this tutorial, I will present you my alternative solution to the select form tag. It’s css-only and it looks simple but really nice. We will use a list of radio elements, styled as a drop-down list, that will look and behave similarly to the select element.
Of course you have to provide some fallback for mobile devices (and IE8 if you wish). I discuss that briefly in the final part of this tutorial.
Check the demo and choose your favorite beer.
Step 1 – HTML
Here is the html we use within a form
<fieldset class="radio-container">
<div class="radio-options">
<div class="toggle">Choose your beer</div>
<ul>
<li>
<input type="radio" name="my-beer" id="choice1" value="choice1">
<label for="choice1">Cul Dorcha</label>
</li>
<li>
<input type="radio" name="my-beer" id="choice2" value="choice2">
<label for="choice2">Rowers Red Ale</label>
</li>
<li>
<input type="radio" name="my-beer" id="choice3" value="choice3">
<label for="choice3">Belfast Ale</label>
</li>
<li>
<input type="radio" name="my-beer" id="choice4" value="choice4">
<label for="choice4">O'Hara Irish Stout</label>
</li>
</ul>
</div>
</fieldset>
Step 2 – The idea
To make things simple I tried to “sketch” my idea. I hope it is clear enough.
Step 3 – CSS
Let’s add some css that reflect the idea presented in the scheme above. For the brevity of this tutorial I omit some parts of the css (e.g. triangle arrows) that only add some visual flavor – you’ll find the complete version in the attached files.
Note that the vendor prefixes are omitted for the same reason.
For the outer containter (“.radio-container”) we’ll have
radio-container {
position: relative;
height: 4em; /* 3em (being the max-height of the inner container) + 1em ("margin") */
}
.radio-container:hover {
z-index: 9999; }
And for the inner one
.radio-options {
position: absolute;
max-height: 3em;
width: 100%;
overflow: hidden;
transition: 0.7s;
}
.radio-options:hover {
max-height: 100em;
}
Next
.radio-options .toggle {
position: relative;
cursor: pointer;
padding: 0.75em;
background: darkgreen;
border-radius: 10px;
z-index: 1; }
/* li are stacked at the same position as .toggle, only .toggle is visible */
.radio-options li {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.radio-options label {
display: block;
opacity: 0;
transition: 0s; }
We hide the inputs, we could just use display : none, but that would not work in browsers (some mobile ones) where clicking the label does not focus the associated input.
.radio-options input {
position: absolute;
top: 0;
left: 0;
width: 300px;
height: 3em;
opacity: 0;
z-index:1;
cursor: pointer;
}
Step 4 – What happens on hover – CSS continued
Now let’s look closer at what happens on hover, already : .radio-container gets a high z-index and .radio-options increases its max-height, we’ll add
/* li elements have a normal flow within the .radio-options container */
.radio-options:hover li {
position: relative; }
.radio-options:hover label {
opacity: 1;
transition: 0.5s; }
Step 5 – input:checked
To style the checked option we will use the general sibling selector. It uses a tilde character combinator (E ~ F) and matches elements that are siblings of a given element. The first element (E) has to occur before the second (F) one and they have to share the same parent (li items in our case).
If one of the radio is checked, we’ll see its label instead of the toggle :
.radio-options input:checked ~ label {
position: absolute;
top: 0;
left: 0;
right: 0;
opacity: 1;
/* is above the .toggle so is visible */
z-index: 2;
/* has tha same styles as .toggle */
padding: 0.75em;
background: darkgreen;
border-radius: 10px; }
On hover it returns to the normal flow
.radio-options:hover input:checked ~ label {
position: static;
border-radius: 0; }
Step 6 – What about mobile devices
Since our element is activated on hover you’ll have to provide some fallback for touch devices. One solution is to leave the radio labels visible all the time not only on hover.
Here is my solution to keep the drop-down list, I detect the touch devices with a custom modernizr build and add the following script
$(document).ready(function(){
if (Modernizr.touch) {
$(".radio-options").bind("click", function(event) {
if (!($(this).parent('.radio-container').hasClass("active"))) {
$(this).parent('.radio-container').addClass("active");
event.stopPropagation();
}
});
$(".toggle").bind("click", function(){
$(this).parents('.radio-container').removeClass("active");
return false;
});
}
});
In my css I modify every :hover definition like that
.no-touch .radio-container:hover, .active.radio-container {
z-index: 9999; }
.no-touch .radio-options:hover, .active .radio-options {
max-height: 100em;
}
.no-touch .radio-options:hover li, .active .radio-options li {
position: relative; }
.no-touch .radio-options:hover label, .active .radio-options label {
opacity: 1;
transition: 0.5s; }
....
Step 7 – What about IE8
Again, it’s up to you, the fallback solutions are not the main part of this tutorial. Here is my approach:
<!-- [if (IE 8)]>
<script>
$(document).ready(function(){
$(".radio-options li").bind("click", function() {
$(this).siblings(".checked").removeClass("checked");
$(this).addClass("checked");
});
});
</script>
<![endif]-->
I have to add to my css the .checked class declarations, e.g. (see the attached files for the complete version):
.radio-options .checked label {
position: absolute;
top: 0;
left: 0;
right: 0;
padding: 0.75em;
background: #1b9e4d;
visibility: visible;
z-index: 2; }
....
That’s all. I hope you’ll find this technique useful, let me know what you think. Thanks.
Terms of use :
You may use the effects demonstrated in tutorials in your own work, both commercial or non-commercial without any attribution. You may not reproduce entire nor large parts of our tutorials. The outcome of our tutorials may not be re-saled nor redistributed.