View demo Download source
Hi guys! I’m back with another article just for you, and CSS related
of course! This time, we are going to talk (and do stuff!) about
something a bit more practical than button switches: drop-down lists.
The point of this tutorial is to show how to create nice drop-downs
without any image but with CSS only. We’ll add some line of jQuery to
make them work.
A few things before starting:
- You won’t see any vendor prefixes in the CSS snippets, but you will, of course, find them in the files.
- I personally use the box-model where [width] = [element-width] +
[padding] + [borders]. I activate it with the following snippet:
1
2
3
4
5
6
|
*,
*:after,
*:before {
box-sizing: border-box;
}
|
How do we start?
First question: what do we need to create a drop-down? In general,
we’ll use a division with a span and an unordered list for the drop-down
list (we might tweak this for some examples):
1
2
3
4
5
6
7
8
|
< div class = "wrapper-dropdown" >
< span >I'm kinda the label!</ span >
< ul class = "dropdown" >
< li >I'm hidden!</ li >
< li >Me too!</ li >
< li >So do I.</ li >
</ ul >
</ div >
|
The JavaScript
For now and before everything else, we need some JavaScript to make
this work. Since it’s basically the same JS snippet for all demos, let’s
deal with it now:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
obj.dd.on( 'click' , function (event){
$( this ).toggleClass( 'active' );
return false ;
});
$( function () {
var dd = new DropDown( $( '#dd' ) );
$(document).click( function () {
$( '.wrapper-dropdown-1' ).removeClass( 'active' );
});
});
|
So what does this script do exactly? First, it toggles a class called
.active
when you click on the wrapper. It means if the wrapper doesn’t have the
.active
class, it adds it, and if it does, it removes it.
Second thing, it replicates the default behavior of a select
drop-down by closing it if you click anywhere else on the screen.
Basically, the script says if we click on a child from the <html>
tag (so every single node on the DOM), the wrapper loses its
.active
class. But we prevent this behavior on the wrapper itself by stopping the propagation. Fairly simple, right?
Well, now we understand how it works, I guess it’s time to create some neat drop-downs!
Example 1
Let’s start with something simple: a basic drop-down for gender. Let’s look at the markup first:
The Markup
We need a few things: a wrapper, a (hidden) drop-down list and a
“label” which we will wrap into a span. We use anchors because it seems
semantically correct to me, but we could have also used another tag.
1
2
3
4
5
6
7
|
< div id = "dd" class = "wrapper-dropdown-1" tabindex = "1" >
< span >Gender</ span >
< ul class = "dropdown" >
< li >< a href = "#" >Male</ a ></ li >
< li >< a href = "#" >Female</ a ></ li >
</ ul >
</ div >
|
The CSS
Let’s dig into the CSS which is our focus in this tutorial. We will start with the wrapper:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
.wrapper-dropdown {
position : relative ;
width : 200px ;
padding : 10px ;
margin : 0 auto ;
background : #9bc7de ;
color : #fff ;
outline : none ;
cursor : pointer ;
font-weight : bold ;
}
|
We did a few things here. First we set a width to our dropdown and
some paddings/margins. Next, we gave it some styles. And finally, we set
some font settings, which will cascade to the dropdown itself.
Let’s finish with the “label” by adding the little arrow on the right with a pseudo-element (styling purpose = no extra markup).
1
2
3
4
5
6
7
8
9
10
11
12
|
.wrapper-dropdown:after {
content : "" ;
width : 0 ;
height : 0 ;
position : absolute ;
right : 16px ;
top : 50% ;
margin-top : -6px ;
border-width : 6px 0 6px 6px ;
border-style : solid ;
border-color : transparent #fff ;
}
|
I think we all know how to create a little triangle with CSS thanks
to some border tricks. It’s a hack yep, but it works pretty well so why
not? Nothing much there then: a little white down arrow on the right of
the wrapper.
We have a nice little button there, but without an actual drop-down it has no point really. So let’s deal with our list!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
.wrapper-dropdown -1 .dropdown {
position : absolute ;
top : 100% ;
left : 0 ;
right : 0 ;
background : #fff ;
font-weight : normal ;
opacity: 0 ;
pointer-events: none ;
}
|
What did we just do? We give the drop-down absolute positioning and placed it just behind the button (
top: 100%;
).
We gave it the same width as the button with the left and right values
set to 0. And more importantly, we hide it by reducing its opacity to 0.
What about pointer-events? Not seeing something doesn’t mean it’s not
there. Setting pointer-events to none prevents clicking on the dropdown
while it’s “hidden”.
Let’s give some styles to the list elements inside the dropdown:
1
2
3
4
5
6
7
8
9
10
11
|
.wrapper-dropdown -1 .dropdown li a {
display : block ;
text-decoration : none ;
color : #9e9e9e ;
padding : 10px 20px ;
}
.wrapper-dropdown -1 .dropdown li:hover a {
background : #f3f8f8 ;
}
|
Okay, so we have a nice button and a nice hidden drop-down menu. Now
we have to deal with the “open” case when you click on the button to
show the options.
With JavaScript we toggle a class
.active
when we click on the button, so based on this class we can change our CSS to show the drop-down.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
.wrapper-dropdown -1 .active .dropdown {
opacity: 1 ;
pointer-events: auto ;
}
.wrapper-dropdown -1 .active:after {
border-color : #9bc7de transparent ;
border-width : 6px 6px 0 6px ;
margin-top : -3px ;
}
.wrapper-dropdown -1 .active {
background : #9bc7de ;
background : linear-gradient(to right , #9bc7de 0% , #9bc7de 78% , #ffffff 78% , #ffffff 100% );
}
|
Three things here:
- First, we make the drop-down appear by turning its opacity to 1.
Don’t forget to set the pointer-event to auto to enable the interaction
with it!
- Next, we change the direction and the color of the little arrow.
- Then, we change the background behind the arrow by using a clever gradient on the button. Isn’t that nice?
The JavaScript
Last but not least, we also have to add another JavaScript snippet to make the button display the selected value.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
function DropDown(el) {
this .dd = el;
this .placeholder = this .dd.children( 'span' );
this .opts = this .dd.find( 'ul.dropdown > li' );
this .val = '' ;
this .index = -1;
this .initEvents();
}
DropDown.prototype = {
initEvents : function () {
var obj = this ;
obj.dd.on( 'click' , function (event){
$( this ).toggleClass( 'active' );
return false ;
});
obj.opts.on( 'click' , function (){
var opt = $( this );
obj.val = opt.text();
obj.index = opt.index();
obj.placeholder.text( 'Gender: ' + obj.val);
});
},
getValue : function () {
return this .val;
},
getIndex : function () {
return this .index;
}
}
|
Very simple code here: when an element is clicked we get its value and display it in the “label”.
Example 2
What a beautiful little drop-down to choose your way to sign in! I
know, we use to have fancy buttons for that but let’s try something new,
shall we?
The Markup
1
2
3
4
5
6
7
|
< div id = "dd" class = "wrapper-dropdown-2" >Sign in with
< ul class = "dropdown" >
< li >< a href = "#" >< i class = "icon-twitter icon-large" ></ i >Twitter</ a ></ li >
< li >< a href = "#" >< i class = "icon-github icon-large" ></ i >Github</ a ></ li >
< li >< a href = "#" >< i class = "icon-facebook icon-large" ></ i >Facebook</ a ></ li >
</ ul >
</ div >
|
The <i> tags are used to display little icons from
FontAwesome.
I won’t explain all the FontAwesome stuff here because it has already
been covered multiple times, I guess. Just make sure it works. :P
The CSS
Let’s start with the wrapper, shall we? Pretty much the same as the
wrapper in the previous example. Note the 5px left border, it’s
important for the following. ;)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
.wrapper-dropdown -2 {
position : relative ;
width : 200px ;
margin : 0 auto ;
padding : 10px 15px ;
background : #fff ;
border-left : 5px solid grey;
cursor : pointer ;
outline : none ;
}
|
Now the little arrow. Exactly the same as before:
1
2
3
4
5
6
7
8
9
10
11
12
|
.wrapper-dropdown -2: after {
content : "" ;
width : 0 ;
height : 0 ;
position : absolute ;
right : 16px ;
top : 50% ;
margin-top : -3px ;
border-width : 6px 6px 0 6px ;
border-style : solid ;
border-color : grey transparent ;
}
|
And here comes the drop-down list. Again, it’s pretty much the same thing like in the previous example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
.wrapper-dropdown -2 .dropdown {
position : absolute ;
top : 100% ;
left : -5px ;
right : 0px ;
background : white ;
transition: all 0.3 s ease-out;
list-style : none ;
opacity: 0 ;
pointer-events: none ;
}
|
Please note the transition that we’ll use to make the drop-down
progressively appear (animate) instead of simply pop up like in the
first demo.
Some styles for the links and the icons:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
.wrapper-dropdown -2 .dropdown li a {
display : block ;
text-decoration : none ;
color : #333 ;
border-left : 5px solid ;
padding : 10px ;
transition: all 0.3 s ease-out;
}
.wrapper-dropdown -2 .dropdown li:nth-child( 1 ) a {
border-left-color : #00ACED ;
}
.wrapper-dropdown -2 .dropdown li:nth-child( 2 ) a {
border-left-color : #4183C4 ;
}
.wrapper-dropdown -2 .dropdown li:nth-child( 3 ) a {
border-left-color : #3B5998 ;
}
.wrapper-dropdown -2 .dropdown li i {
margin-right : 5px ;
color : inherit;
vertical-align : middle ;
}
.wrapper-dropdown -2 .dropdown li:hover a {
color : grey;
}
|
We give the links a left border with a color based on the brand they
stand for. The text is slightly indented to the right via a margin-right
on the icons.
And now, the expanded state. Pretty straight forward: the arrow
changes direction, and the drop-down list becomes visible. Thanks to the
transition property on the drop-down, it appears progressively (opacity
animates from 0 to 1).
1
2
3
4
5
6
7
8
|
.wrapper-dropdown -2 .active:after {
border-width : 0 6px 6px 6px ;
}
.wrapper-dropdown -2 .active .dropdown {
opacity: 1 ;
pointer-events: auto ;
}
|
The JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function DropDown(el) {
this .dd = el;
this .initEvents();
}
DropDown.prototype = {
initEvents : function () {
var obj = this ;
obj.dd.on( 'click' , function (event){
$( this ).toggleClass( 'active' );
event.stopPropagation();
});
}
}
|
Example 3
This one is probably the one which comes the closest to a regular
select element. Indeed, when you pick something, the label’s default
value is replaced by the picked value. On a side note: it looks great
doesn’t it?
The Markup
1
2
3
4
5
6
7
8
|
< div id = "dd" class = "wrapper-dropdown-3" tabindex = "1" >
< span >Transport</ span >
< ul class = "dropdown" >
< li >< a href = "#" >< i class = "icon-envelope icon-large" ></ i >Classic mail</ a ></ li >
< li >< a href = "#" >< i class = "icon-truck icon-large" ></ i >UPS Delivery</ a ></ li >
< li >< a href = "#" >< i class = "icon-plane icon-large" ></ i >Private jet</ a ></ li >
</ ul >
</ div >
|
Not much more than before. Let’s go with the CSS!
The CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
.wrapper-dropdown-3 {
/* Size and position */
position: relative;
width: 200px;
margin: 0 auto;
padding: 10px;
/* Styles */
background: #fff;
border-radius: 7px;
border: 1px solid rgba(0,0,0,0.15);
box-shadow: 0 1px 1px rgba(50,50,50,0.1);
cursor: pointer;
outline: none;
/* Font settings */
font-weight: bold;
color: #8AA8BD;
}
|
Here we use some borders, a box-shadow and rounded corners. We need the little arrow:
1
2
3
4
5
6
7
8
9
10
11
12
|
.wrapper-dropdown -3: after {
content : "" ;
width : 0 ;
height : 0 ;
position : absolute ;
right : 15px ;
top : 50% ;
margin-top : -3px ;
border-width : 6px 6px 0 6px ;
border-style : solid ;
border-color : #8aa8bd transparent ;
}
|
This is the same as before, so let’s skip forward to the drop-down and its children.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
.wrapper-dropdown -3 .dropdown {
position : absolute ;
top : 140% ;
left : 0 ;
right : 0 ;
background : white ;
border-radius: inherit;
border : 1px solid rgba( 0 , 0 , 0 , 0.17 );
box-shadow: 0 0 5px rgba( 0 , 0 , 0 , 0.1 );
font-weight : normal ;
transition: all 0.5 s ease-in;
list-style : none ;
opacity: 0 ;
pointer-events: none ;
}
.wrapper-dropdown -3 .dropdown li a {
display : block ;
padding : 10px ;
text-decoration : none ;
color : #8aa8bd ;
border-bottom : 1px solid #e6e8ea ;
box-shadow: inset 0 1px 0 rgba( 255 , 255 , 255 , 1 );
transition: all 0.3 s ease-out;
}
.wrapper-dropdown -3 .dropdown li i {
float : right ;
color : inherit;
}
.wrapper-dropdown -3 .dropdown li:first-of-type a {
border-radius: 7px 7px 0 0 ;
}
.wrapper-dropdown -3 .dropdown li:last-of-type a {
border-radius: 0 0 7px 7px ;
border : none ;
}
.wrapper-dropdown -3 .dropdown li:hover a {
background : #f3f8f8 ;
}
|
A few notes here:
- We use a little box-shadow on the links in order to create a subtle light effect on their top.
- To prevent this shadow to go out of the menu, we give the first link rounded corners.
- We remove the border of the last link to avoid a 1px weird border at the bottom of the dropdown.
- We don’t change the markup to place icons on the right: a simple
float: right
works like a charm.
Everything looks right except the little arrow on the top right of
the drop-down. This arrow is important: without it, the dropdown looks
like it’s floating with no connection to the button.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
.wrapper-dropdown -3 .dropdown:after {
content : "" ;
width : 0 ;
height : 0 ;
position : absolute ;
bottom : 100% ;
right : 15px ;
border-width : 0 6px 6px 6px ;
border-style : solid ;
border-color : #fff transparent ;
}
.wrapper-dropdown -3 .dropdown:before {
content : "" ;
width : 0 ;
height : 0 ;
position : absolute ;
bottom : 100% ;
right : 13px ;
border-width : 0 8px 8px 8px ;
border-style : solid ;
border-color : rgba( 0 , 0 , 0 , 0.1 ) transparent ;
}
|
Why are we using two pseudo-elements for this arrow? We want to
create the border around it. Basically we create a white triangle
sitting on top of a grey and slightly larger one. This way, it looks
like there is only one little arrow with a border.
An now the expanded state. Always the same thing. However, note how we set the transition to the
.dropdown
a bit longer than usual (0.5s instead of 0.3s). That way, the opening of the menu is very smooth.
1
2
3
4
|
.wrapper-dropdown -3 .active .dropdown {
opacity: 1 ;
pointer-events: auto ;
}
|
The JavaScript
To finish this demo, we need to add a little bit of JavaScript to
replace the default value of the button by the selected one. We saw how
to do it in the first example, but since we don’t keep the “Transport”
word here, the JS is very slightly different.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
function DropDown(el) {
this .dd = el;
this .placeholder = this .dd.children( 'span' );
this .opts = this .dd.find( 'ul.dropdown > li' );
this .val = '' ;
this .index = -1;
this .initEvents();
}
DropDown.prototype = {
initEvents : function () {
var obj = this ;
obj.dd.on( 'click' , function (event){
$( this ).toggleClass( 'active' );
return false ;
});
obj.opts.on( 'click' , function (){
var opt = $( this );
obj.val = opt.text();
obj.index = opt.index();
obj.placeholder.text(obj.val);
});
},
getValue : function () {
return this .val;
},
getIndex : function () {
return this .index;
}
}
|
Example 4
Looks different, doesn’t it? For this one, I thought it would be cool
to create a little to-do-list instead of a select drop-down or a
drop-down menu. Nothing spectacular, but different than previous demos
for sure. ;)
The Markup
1
2
3
4
5
6
7
|
< div id = "dd" class = "wrapper-dropdown-4" >To do
< ul class = "dropdown" >
< li >< input type = "checkbox" id = "el-1" name = "el-1" value = "donut" >< label for = "el-1" >Eat a donut</ label ></ li >
< li >< input type = "checkbox" id = "el-2" name = "el-2" value = "neighbour" >< label for = "el-2" >Spy on my neighbours</ label ></ li >
< li >< input type = "checkbox" id = "el-3" name = "el-3" value = "T-rex" >< label for = "el-3" >Feed my T-Rex</ label ></ li >
</ ul >
</ div >
|
No more links. No more icons. For each element, we have two things: a checkbox linked to a label.
The CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
|
.wrapper-dropdown -4 {
position : relative ;
width : 270px ;
margin : 0 auto ;
padding : 10px 10px 10px 30px ;
background : #fff ;
border : 1px solid silver ;
cursor : pointer ;
outline : none ;
}
|
Nothing to say except that we use an important left padding to create
enough space for the red lines. Now, the little arrow on the right:
1
2
3
4
5
6
7
8
9
10
11
12
|
.wrapper-dropdown -4: after {
content : "" ;
width : 0 ;
height : 0 ;
position : absolute ;
right : 10px ;
top : 50% ;
margin-top : -3px ;
border-width : 6px 6px 0 6px ;
border-style : solid ;
border-color : #ffaa9f transparent ;
}
|
The dropdown. I’m pretty sure you’re getting used to it. :)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
.wrapper-dropdown -4 .dropdown {
position : absolute ;
top : 100% ;
margin-top : 1px ;
left : -1px ;
right : -1px ;
background : white ;
border : 1px solid silver ;
border-top : none ;
list-style : none ;
transition: all 0.3 s ease-out;
opacity: 0 ;
pointer-events: none ;
}
|
We need to set the margin-top to 1px because we need to push it a bit
down due to the border of the wrapper. The left is set to -1px to pull
the drop-down into position and we’ll give it the same border like its
parent, except that we take away the top one.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
.wrapper-dropdown -4 .dropdown li {
position : relative ;
}
.wrapper-dropdown -4 .dropdown li label {
display : block ;
padding : 10px 10px 10px 30px ;
border-bottom : 1px dotted #1ccfcf ;
transition: all 0.3 s ease-out;
}
.wrapper-dropdown -4 .dropdown li:last-of-type label {
border : none ;
}
.wrapper-dropdown -4 .dropdown li input {
position : absolute ;
display : block ;
right : 10px ;
top : 50% ;
margin-top : -8px ;
}
.wrapper-dropdown -4 .dropdown li:hover label {
background : #f0f0f0 ;
}
.wrapper-dropdown -4 .dropdown li input:checked ~ label {
color : grey;
text-decoration : line-through ;
}
|
Checkboxes are absolutely placed on the middle right of each line but
since they are linked to labels, you can click wherever you want on the
line to toggle them.
When a checkbox is checked, the following respective label becomes grey and crossed-out. Simple but effective.
And now, we have to deal with the two thin red lines on the left of
our little notebook. There are two ways to do this: one with
pseudo-elements and one with gradients. Let’s look at both of them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
.wrapper-dropdown -4 .dropdown:before,
.wrapper-dropdown -4: before {
content : "" ;
width : 4px ;
height : 100% ;
position : absolute ;
top : 0 ;
left : 15px ;
border : 1px solid #ffaa9f ;
border-top : none ;
border-bottom : none ;
z-index : 2 ;
}
.wrapper-dropdown -4 .dropdown,
.wrapper-dropdown -4 {
background : linear-gradient( left , white 5% , #ffaa9f 5% , #ffaa9f 5.3% , white 5.3% , white 6.5% , #ffaa9f 6.5% , #ffaa9f 6.8% , white 6.8% );
}
.wrapper-dropdown -4 .dropdown li:hover label {
background : linear-gradient( left , #f0F0F0 5% , #ffaa9f 5% , #ffaa9f 5.3% , #f0F0F0 5.3% , #f0F0F0 6.5% , #ffaa9f 6.5% , #ffaa9f 6.8% , #f0F0F0 6.8% );
}
|
The first method creates a pseudo-element (two actually: one for the
button and one for the dropdown) with left and right borders sitting on
top of everything else.
The second method fakes the red lines with a gradient on both, the wrapper and the dropdown.
So which one is better? Probably the first one, because if you want to
change the hover effect on the list elements, you have to change the
gradient which is pretty awful. Plus, pseudo-elements have a way better
browser support (back to IE8) than gradients (not supported until IE10).
Let’s end it with the expanded state. Nothing new here.
1
2
3
4
5
6
7
8
9
10
|
.wrapper-dropdown -4 .active:after {
border-width : 0 6px 6px 6px ;
}
.wrapper-dropdown -4 .active .dropdown {
opacity: 1 ;
pointer-events: auto ;
}
|
The JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
function DropDown(el) {
this .dd = el;
this .opts = this .dd.find( 'ul.dropdown > li' );
this .val = [];
this .index = [];
this .initEvents();
}
DropDown.prototype = {
initEvents : function () {
var obj = this ;
obj.dd.on( 'click' , function (event){
$( this ).toggleClass( 'active' );
event.stopPropagation();
});
obj.opts.children( 'label' ).on( 'click' , function (event){
var opt = $( this ).parent(),
chbox = opt.children( 'input' ),
val = chbox.val(),
idx = opt.index();
($.inArray(val, obj.val) !== -1) ? obj.val.splice( $.inArray(val, obj.val), 1 ) : obj.val.push( val );
($.inArray(idx, obj.index) !== -1) ? obj.index.splice( $.inArray(idx, obj.index), 1 ) : obj.index.push( idx );
});
},
getValue : function () {
return this .val;
},
getIndex : function () {
return this .index;
}
}
|
Example 5
Our last example is a little drop-down menu for some admin panel. For
this one, we will use a different animation when we toggle it. Instead
of appearing/disappearing, it will slide up and down.
The markup
1
2
3
4
5
6
7
|
< div id = "dd" class = "wrapper-dropdown-5" tabindex = "1" >John Doe
< ul class = "dropdown" >
< li >< a href = "#" >< i class = "icon-user" ></ i >Profile</ a ></ li >
< li >< a href = "#" >< i class = "icon-cog" ></ i >Settings</ a ></ li >
< li >< a href = "#" >< i class = "icon-remove" ></ i >Log out</ a ></ li >
</ ul >
</ div >
|
The CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
.wrapper-dropdown -5 {
position : relative ;
width : 200px ;
margin : 0 auto ;
padding : 12px 15px ;
background : #fff ;
border-radius: 5px ;
box-shadow: 0 1px 0 rgba( 0 , 0 , 0 , 0.2 );
cursor : pointer ;
outline : none ;
transition: all 0.3 s ease-out;
}
.wrapper-dropdown -5: after {
content : "" ;
width : 0 ;
height : 0 ;
position : absolute ;
top : 50% ;
right : 15px ;
margin-top : -3px ;
border-width : 6px 6px 0 6px ;
border-style : solid ;
border-color : #4cbeff transparent ;
}
|
Basic stuff there. Let’s go to the dropdown, which is a little bit different than usual.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
.wrapper-dropdown -5 .dropdown {
position : absolute ;
top : 100% ;
left : 0 ;
right : 0 ;
background : #fff ;
border-radius: 0 0 5px 5px ;
border : 1px solid rgba( 0 , 0 , 0 , 0.2 );
border-top : none ;
border-bottom : none ;
list-style : none ;
transition: all 0.3 s ease-out;
max-height : 0 ;
overflow : hidden ;
}
|
This time, we don’t turn the opacity to 0 to hide the menu. We set
its max-height to 0 and its overflow to hidden. Why its max-height and
not its height? Because we don’t know the exact height of the expanded
drop-down.
So no need of pointer-events this time since the menu is really not there.
Quick and simple styles for the list elements.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
.wrapper-dropdown -5 .dropdown li {
padding : 0 10px ;
}
.wrapper-dropdown -5 .dropdown li a {
display : block ;
text-decoration : none ;
color : #333 ;
padding : 10px 0 ;
transition: all 0.3 s ease-out;
border-bottom : 1px solid #e6e8ea ;
}
.wrapper-dropdown -5 .dropdown li:last-of-type a {
border : none ;
}
.wrapper-dropdown -5 .dropdown li i {
margin-right : 5px ;
color : inherit;
vertical-align : middle ;
}
.wrapper-dropdown -5 .dropdown li:hover a {
color : #57a9d9 ;
}
|
And now, the active state:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
.wrapper-dropdown -5 .active {
border-radius: 5px 5px 0 0 ;
background : #4cbeff ;
box-shadow: none ;
border-bottom : none ;
color : white ;
}
.wrapper-dropdown -5 .active:after {
border-color : #82d1ff transparent ;
}
.wrapper-dropdown -5 .active .dropdown {
border-bottom : 1px solid rgba( 0 , 0 , 0 , 0.2 );
max-height : 400px ;
}
|
When the dropdown is open, we change the bottom corners of the
button, its color, its arrow direction and arrow color and remove both,
its box-shadow and its border.
And to show the menu, we set the max-height of the dropdown to 400px. We
could have set it to 500px, 1000px ou 1000000px; it doesn’t matter as
long as it’s taller than its height.
The JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function DropDown(el) {
this .dd = el;
this .initEvents();
}
DropDown.prototype = {
initEvents : function () {
var obj = this ;
obj.dd.on( 'click' , function (event){
$( this ).toggleClass( 'active' );
event.stopPropagation();
});
}
}
|
Fallbacks
Okay guys, we now have 5 awesome drop-downs working like a charm, but what about legacy browsers?
These browsers don’t understand the opacity property. And if some of
these do with filters, they don’t understand pointer-events. It sucks
and this is why you might want to put a fallback in place.
This is where our friend
Modernizr
is coming into play. Roughly, for those who don’t know what Modernizr
is, it’s a JavaScript library that detects HTML5 and CSS3 features in
the user’s browser.
Thanks to this awesome script, we can basically tell the browser
“if you don’t support *this* property, then do *that*”.
With Modernizr we can have classes added to the html, for example,
“no-pointerevents” if there is no support for pointer-events (make sure
to select that in the non-core detects when you build your Modernizr).
The following is an example of how we can manage the fallback for
browsers that don’t support certain CSS properties:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
.no-opacity .wrapper-dropdown -1 .dropdown,
.no-pointerevents .wrapper-dropdown -1 .dropdown {
display : none ;
opacity: 1 ;
pointer-events: auto ;
}
.no-opacity .wrapper-dropdown -1 .active .dropdown,
.no-pointerevents .wrapper-dropdown -1 .active .dropdown {
display : block ;
}
|
If the browser doesn’t support either opacity or pointer-events, then we hide the drop-down with a simple
display: none;
.
If the browser doesn’t support opacity but does support pointer-events, we set those to
auto
to allow the user to click on the menu once expanded.
In the other hand, if the browser doesn’t support pointer-events but
does support opacity, we set it to 1 to make the dropdown appear once
the
.active
class is toggled.
When the
.active
class is toggled, we show the drop-down with
display: block;
. Easy peasy!
Note: of course this doesn’t apply for the demo with the
max-height animation. Only for the others with opacity and
pointer-events.