Custom Drop-Down List Styling

A tutorial on how to create some custom drop-down lists. We’ll show you five examples with different looking drop-down menus and lists for various purposes.

Custom Drop-Down List Styling

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:
    		*,
    		*: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):

<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:

	//...

	obj.dd.on('click', function(event){
		$(this).toggleClass('active');
		return false;
	});

	//...

	$(function() {

		var dd = new DropDown( $('#dd') );

		$(document).click(function() {
			// all dropdowns
			$('.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.

<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>

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

The CSS

Let’s dig into the CSS which is our focus in this tutorial. We will start with the wrapper:

.wrapper-dropdown {
    /* Size and position */
    position: relative; /* Enable absolute positioning for children and pseudo elements */
    width: 200px;
    padding: 10px;
    margin: 0 auto;

    /* Styles */
    background: #9bc7de;
    color: #fff;
    outline: none;
    cursor: pointer;

    /* Font settings */
    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).

.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!

.wrapper-dropdown-1 .dropdown {
	/* Size & position */
    position: absolute;
    top: 100%;
    left: 0; /* Size */
    right: 0; /* Size */

    /* Styles */
    background: #fff;
    font-weight: normal; /* Overwrites previous font-weight: bold; */

    /* Hiding */
    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:

.wrapper-dropdown-1 .dropdown li a {
    display: block;
    text-decoration: none;
    color: #9e9e9e;
    padding: 10px 20px;
}

/* Hover state */
.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.

/* Active state */
.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.

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

<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. 😛

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. 😉

.wrapper-dropdown-2 {
    /* Size and position */
    position: relative; /* Enable absolute positioning for children and pseudo elements */
    width: 200px;
    margin: 0 auto;
    padding: 10px 15px;

    /* Styles */
    background: #fff;
    border-left: 5px solid grey;
    cursor: pointer;
    outline: none;
}

Now the little arrow. Exactly the same as before:

.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:

.wrapper-dropdown-2 .dropdown {
  /* Size & position */
    position: absolute;
    top: 100%;
    left: -5px;
    right: 0px;

    /* Styles */
    background: white;
    transition: all 0.3s ease-out;
    list-style: none;

    /* Hiding */
    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:

.wrapper-dropdown-2 .dropdown li a {
    display: block;
    text-decoration: none;
    color: #333;
    border-left: 5px solid;
    padding: 10px;
    transition: all 0.3s 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;
}

/* Hover state */

.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).

.wrapper-dropdown-2.active:after {
    border-width: 0 6px 6px 6px;
}

.wrapper-dropdown-2.active .dropdown {
    opacity: 1;
    pointer-events: auto;
}

The JavaScript

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

<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

.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:

.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.

.wrapper-dropdown-3 .dropdown {
  /* Size & position */
    position: absolute;
    top: 140%;
    left: 0;
    right: 0;

    /* Styles */
    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.5s ease-in;
    list-style: none;

    /* Hiding */
    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.3s 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;
}

/* Hover state */

.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.

.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.

.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.

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

<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

.wrapper-dropdown-4 {
    /* Size and position */
    position: relative;
    width: 270px;
    margin: 0 auto;
    padding: 10px 10px 10px 30px;

    /* Styles */
    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:

.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. 🙂

.wrapper-dropdown-4 .dropdown {
    /* Size & position */
    position: absolute;
    top: 100%;
    margin-top: 1px; /* border of wrapper */
    left: -1px;
    right: -1px;

    /* Styles */
    background: white;
    border: 1px solid silver;
    border-top: none;
    list-style: none;
    transition: all 0.3s ease-out;
  
    /* Hiding */
    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.

.wrapper-dropdown-4 .dropdown li {
    position: relative; /* Enable absolute positioning for checkboxes */
}

.wrapper-dropdown-4 .dropdown li label {
    display: block;
    padding: 10px 10px 10px 30px; /* Same padding as the button */
    border-bottom: 1px dotted #1ccfcf;
    transition: all 0.3s ease-out;
}

.wrapper-dropdown-4 .dropdown li:last-of-type label {
    border: none;
}

.wrapper-dropdown-4 .dropdown li input /* Checkboxes */ {
    position: absolute;
    display: block;
    right: 10px;
    top: 50%;
    margin-top: -8px;
}

/* Hover state */

.wrapper-dropdown-4 .dropdown li:hover label {
    background: #f0f0f0;
}

/* Checked state */

.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.

/* Red lines: the pseudo-elements way */
.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;
}

/* OR: */
/* Red lines: the gradients way */

.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.

/* Active state */

.wrapper-dropdown-4.active:after {
    border-width: 0 6px 6px 6px;
}

.wrapper-dropdown-4.active .dropdown {
    opacity: 1;
    pointer-events: auto;
}

The JavaScript

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

<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

.wrapper-dropdown-5 {
    /* Size & position */
    position: relative;
    width: 200px;
    margin: 0 auto;
    padding: 12px 15px;

    /* Styles */
    background: #fff;
    border-radius: 5px;
    box-shadow: 0 1px 0 rgba(0,0,0,0.2);
    cursor: pointer;
    outline: none;
    transition: all 0.3s ease-out;
}

.wrapper-dropdown-5:after { /* Little arrow */
    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.

.wrapper-dropdown-5 .dropdown {
    /* Size & position */
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;

    /* Styles */
    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.3s ease-out;

    /* Hiding */
    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.

.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.3s 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;
}

/* Hover state */

.wrapper-dropdown-5 .dropdown li:hover a {
    color: #57a9d9;
}

And now, the active state:

/* Active state */

.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

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:

/* No CSS3 support */

.no-opacity       .wrapper-dropdown-1 .dropdown,
.no-pointerevents .wrapper-dropdown-1 .dropdown {
    display: none;
    opacity: 1; /* If opacity support but no pointer-events support */
    pointer-events: auto; /* If pointer-events support but no pointer-events support */
}

.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.

Final words

I hope this tutorial helped you understand how to make your custom drop-downs. As you can see, it is fairly straight forward and pretty simple both, the CSS and the JavaScript.
Please, don’t forget to use fallbacks or tell your users to enable JavaScript.
Thank you for reading this tutorial. And of course if you have any question or related work to show, please do! 🙂

Kitty Giraudel

Non-binary trans accessibility & diversity advocate, frontend developer, author. Real life cat. She/her.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 146

Comments are closed.
  1. Hugo,

    Great help! Loving the tips. I am attempting to use Demo #2 but I want to use multiple instances of your code in a horizontal menu (see example below) I am having fits with the subitems not showing up. What I want if to have the following:

    Major Item 1 || Major Item 2 || Major Item 3…….
    +Minor Item 1 +Minor Item 4
    +Minor Item 2 +Minor Item 5
    +Minor Item 3

    But what happen is items under Major Item 1 work just fine. They do not on the subsequent Major Items — so Minor Items 4 & 5 never show up. I am sure it’s just me being a bone head but I thought I would ask since it seems like a couple people have had this issue. Thanks in advance and keep the info flowing. Love your tips and tricks – great for a newb.

    Troy

    • Troy, I’d love to see your horizontal menu, I’m trying to accomplish the same thing.

  2. Hugo, really nice stuff! I’ve been working with Demo 3.
    Do you (or someone else) think you could provide the JS required in order to take the selected value and actually use it in the form once submitted?

    Thanks,
    -Kirk

  3. does anyone have any idea why this doesn’t work properly on IE9? Even when it is hidden, the ul.dropdown appears when hover, so I can’t interact with the elements below the drop down list.

  4. Hi
    This is great! Just what I needed …

    Is it possible to make the menu open on mouseover instead of by click?
    /Danny

    • Sure. Just replace obj.dd.on('click', function(event){ ... }); by obj.dd.on('mouseenter', function(event){ ... });. It should do the trick.

  5. Hi Hugo,
    I have worked on demo 5. It works nicely..the thing here is , if i want the list stays still after clicking (not to slide up) then what to do??

    regards,,

  6. umm..,,i tried removing event.stopPropagation(); , it resulted the list didn’t slide down at all…

  7. Hi, Please can you suggest, how to clear the selected value? I’m using 3rd one. Thanks

    • Means, I selected one value and displays it. cool. Now just I need the default label again by another event. How? Thanks in advance.

  8. Wanted to know how to customize this to produce a vertical scrollbar for a list containing say 25 items.

  9. Thanks for this tutorial, I really learned a lot. I made a little drop-down share menu to go next to posts for social sharing (using the Js from 5), it works great on single posts but when I put it in the index loop for the blog only the first post/instance functions and all others do nothing. Is there a way to put more than one DD on the same page? I’m a JS newb.

    Thanks for any help in adavance!

    • I totally figured it out! and learned a lot more about how JS works, only one ID per page so I changed the dd id to a class and it works perfect.

  10. Has anyone found a solution to the Internet Explorer issue? I can see the menu items, but in Internet Explorer, it won’t let me click on them.

    • Hi! It looks like there is some JavaScript issue given what you say. Unfortunately I’m on Mac at home so I won’t be able to have a closer look at it until Monday.

  11. I found a problem when there’s more then one menu on the page…only the first drop menu one works on click, the others won’t..is there any solution for that?

  12. Replace obj.opts.on(‘click’,function() with obj.dd.on(‘click’,’li’,function() extends plugin to new added elements

  13. Hey there, I’m searching for a Drop in a drop, you know what I mean? So, in example 3, i want also a drop in “classic mail”. you know how to do this?? please leave your comment at my mailadress or my blog: *here* (it’s a german bookblog ^^)

  14. This is a beautiful dropdown menu. I really appreciate you sharing this with us. I have an issue that I would like to see if there is a way around it. I have a DDL that will contain about 100 items. This pushes the list WAY down the page. Is there a way to get a scroll bar for bigger lists? Thanks again for a wonderful piece of code.

  15. thax really usefull but i dont want those thumbnail images in dropdown ?

    how to remove those images ?

  16. All, instead of asking the author “can you do this and that”, why don’t you do some research and find out how to make it open on hover, rather than a click, or find out how to add a sub-menu, perhaps you should thank him for posting some great ideas, as this “should” give y’all some inspiration. Learn some CSS and learn some javascript. The code he uses is not all that complicated. I’ve only been designing for not yet 2 years and follow this easily.

    Seems to me there’s a lot of people that just want done for them. Do some research and learn for yourself, I guarantee it’ll make it that more exciting when it’s applied for your customized needs.

  17. Wanted to know how to get a scroll bar verticle on drop down on if data get lot of li tags.
    please need a urgent reply..

  18. man, thanks for this, it is genius work,
    I couldn’t find a better dropdown than these,
    and the code is clean, efficient and so small.
    although, I found a minor bug in dropdown #4, it is javascript related,
    when you click a label, it adds the value of the checkbox to the array “val” in your “Dropdown” object,
    but if you click directly on the checkbox, the object in JS does actually nothing (it stays with the same previous values in the “val” array).
    That is not problem if you want to grab the values directly from the checkbox tags, but is if you wanna grab the values from the “Dropdown” object and work with them it is an issue.
    I have done a workaround to this issue, adding the “input” tag, like this:
    obj.opts.children(‘label,input’).on(‘click’, function()
    and then, ask if the item clicked was the label or actually the checkbox, if it was the label, the function does exactly what you wrote, but if it was the checkbox, the variable “val” declared in the function grabs the value from: $(this).val()
    I still working on it. I was just amazed how clean and efficient your code is in jQuery, I’m not that good in it, neither in css programming.
    Thank you for the contribution, and don’t get lost, I already have your web site address 😉 in case I need good work done in front-end programming, as I’m focused mostly in back-end programming.

  19. I’m using number one and I have everything right, but for some reason when you click the on one of the drop downs it doesn’t change to that drop down in the main box. Do you have any idea what would cause this?

  20. Excellent work! In demo 4 can I make it sticky i.e. it does not close instead just leave the option selected?

  21. hello, beautiful resource.
    I was trying to use it, but if I change the href, once selected, I do not open the url you want.
    What should I change?

  22. Hello I love this drop down! I had a similar problem as someone else – I couldn’t add multiple drop downs and then I saw your response. It was to add some jQuery code. All the menus work now but they all drop at the same time and on top of one another. Is there a solution for this?

  23. Hi there! Awesome tutorial, as many others i liked Demo 3.
    The question i’d like to ask, is there any way to transform this drop-down menu to a jumpmeny so if you click for example on “Classic mail
    ” you move to another page. Or what must i chang in the code to make this happen?
    Sorry maybe for the stupid question, im just a newbie in this stuff))

  24. hey!…..gud onee……but i got a problem!!….menu’s comin….but jquery not working!!

  25. I want the same dropdown on same webpage.
    I have altogether 20 dropdown. Is it possible to have done with one JQuery provided by you.?
    Or I have to create 20 function call with different dropdown name????

  26. Beautiful dropdowns. Issue with multiple dropdowns on a page: if they’re stacked on top of each other (basic search tool, for instance), when you click one it drops over the others beneath it, and unfortunately in this case you can still see the dropdowns beneath causing visual confusion. Working on a solution now — anyone else solved this issue or have any ideas? Thanks!

  27. Amazing work man!!
    Just a small problem..When I use these dropdowns two or three times aligned vertically, the content of the upper one overlaps the other below it..
    Tried a lot to figure it out..Please help me out!!

    Thanks..

    • Manik, Did you find a fix for that? I have got everything to work fine in Firefox and Chrome etc but in IE10 even when the dropdown is inactive it still overlaps the box below irt and you cannot click the box below as the first box takes the click.

      Steve

  28. Hi um need help with disable a item. how a can disable a item to the menu?

    thanks
    from colombia

  29. Hello … awesome work!! I inserted the “wrapper-dropdown-3” … it looks great .. but: I like to use it as a normal nav-menu to jump to other pages. I’ve read now all the comments and yes, may be I have over-read a helpful answer related to it. Since I have not a lot experience with JS … and I guess there is my error … I need help. I love this work of you Hugo … but how can I use it as a normal menu to jump to other html pages?
    Hope really you will help me !

  30. Hi Hugo, I need help on Demo 4 . I noticed that the dropdown closes when checkbox is selected but if I clicked the label the dropdown is still open. I wanted the same behaviour that if I clicked the checkbox the dropdown stays open just like clicking the label. Any ideas how to do it?

  31. Using DEMO 3.
    I can’t get the a tags to actually link anywhere.
    I replaced <a href=”#” rel=”nofollow”>…</a>
    with <a href=”http://www.google.com” rel=”nofollow”> and the link doesn’t go anywhere when clicked.

  32. How to use multiple lists in a row on the same page?
    The first drop-down list does not cover the following, how to solve?
    (Demo 3)

  33. Has anyone come up with a fix to allow the links to work as normal and direct to another page?

    Thanks

  34. hey, this worked fine. But I need to be able to get the selected value after clicking one of the dropdown items. Is there anyway to do that, or could you maybe do this for a select box instead of and ?? thanks