Okay kids, it’s geek time. I discovered a limitation in the dropdown menu when I was tooling around in Bootstrap. I found a way around it that preserved the look and didn’t require me to rewrite the markup, so I thought I’d share.

If you don’t know what Bootstrap is, it’s a CSS and JavaScript framework for web developers. I recently implemented it in the app I wrote for Chrome, Sticky Notes. (I know, I know. Really original name.) Anyhoo, I noticed a problem. If you had a lot of notes, this would happen:

Man, look at that. That sucker goes on forever. So how do you get to the items below the fold? You can’t. So, the first thing I did was give it a max-height. (I think 400px is a nice size, if it matters.) So that led to the next problem…

Oh yeah, overflow. Okay fine, I know how to fix that. Just give it an overflow: hidden or in my case, this works nicely:

.dropdown-menu {
    max-height: 400px;
    overflow: hidden;
    overflow-y: auto;
}

But now this happens:

Do you see it? It has scrollbars now, sure (and nice CSS customized scrollbars, thanks to Webkit), but the little bubble arrow is gone. That’s because the vertical overflow is set to scroll, and that arrow is actually the combination of the list’s ::before and ::after pseudoelements, set to show up outside the list itself.

I suppose it’s not that big of a deal, I mean, they’re just little arrows. It doesn’t detract from functionality, sure. But they have a certain asthetic, and I like it. And I’m gonna fix it, dangit!

Well, it turns out I’m not the only one to notice this. Someone else wrote an alternative dropdown plugin for jQuery, called jQuery Bootstrap-style Dropdowns. But it requires loading yet another plugin (something I didn’t want to do) and reworking the markup (something I didn’t want to do even more, since that note dropdown is built on the fly by other, custom JavaScript).

The pseudoelements on the list item might be hidden, but I could make new ones and put them on the parent link. So I wrote these styles:

a.dropdown-toggle {
    position: relative;
}

The positioning on the parent link is just to set it up for showing the little arrow bubble. On to the important stuff:

a.dropdown-toggle::before {
    content: '';
    display: inline-block;
    border-left: 7px solid transparent;
    border-right: 7px solid transparent;
    border-bottom: 7px solid #CCC;
    border-bottom-color: rgba(0, 0, 0, 0.2);
    position: absolute;
    bottom: -2px;
    right: 7px;
    display: none;
}
a.dropdown-toggle::after {
    content: '';
    display: inline-block;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-bottom: 6px solid white;
    position: absolute;
    bottom: -2px;
    right: 8px;
    z-index: 1001;
    display: none;
}

These are nearly identical to the pseudoelements defined for the list element, just adjusted for positioning along the bottom instead of the top. The last thing is the display: none so that the arrow doesn’t show up all the time. The last thing, then, is to make sure they appear when the list appears:

.open > a.dropdown-toggle::before,
.open > a.dropdown-toggle::after {
    display: block;
}

The dropdown toggles only work when Bootstrap’s menu plugin is active; when you click on a dropdown link, it gives a class of “open” to the link’s parent list item. I can use that, then, to target the opened list’s arrows. The end result, then, is this: a scrollable, max-heighted (it is too a word, I just made it up) dropdown menu with the bubble arrows intact.

Oh snap.

Husband. Daddy. Programmer. Artist. I'm not an expert, I just play one in real life.
  • Michele

    I’m pretty sure I only understood half of what you wrote!!!

  • Nice work. One question, how did you manage to make the scrollbars look that nice? I can see some jQuery plugins around; however, that seems to collide Bootstrap code when I try to aplly them to my Bootstrap dropdowns. Thanks!

  • I used Webkit-specific scrollbar CSS. See here:
    http://css-tricks.com/custom-scrollbars-in-webkit/

  • Thanks! I’ll check it out.

  • Do you have any idea of how to apply this to a sub menu as your current solution only works for a main menu with no sub menus?

  • Hopefully you’re not still waiting on this answer, Jacob, but for others visiting this page – To make it work for the submenu, use this instead:

    .dropdown-submenu:hover > .dropdown-menu {
    max-height: 400px;
    overflow: hidden;
    overflow-y: auto;
    }

  • Yes, that will work for children. But there’s no way to implement this for both the parent menu and its submenu. For that, you’d need a totally different approach.

  • John

    Demo?

  • Great post! It inspired me to come up with another solution that has:

    No JavaScript
    Does not interfere with the layout/CSS of the menu you’re trying to scroll content for
    Works with multiple scroll-menu in the same dropdown-menu
    Works with dropdown-submenu
    Works in responsive mode and mobile/touch enabled
    Allows for static headers and footers that will not scroll with the content using the normal list items
    The scroll-menu will grow dynamically until it reaches the max-height (at which point it will show a vertical scrollbar for each scroll-menu)

    Example: http://jsfiddle.net/XKEmy/
    See comment: https://github.com/twitter/bootstrap/issues/1989

  • Arindam Biswas

    @Paul Nice!

    @ugate thats awesome what you have going in the jsfiddle

  • Nachiket Darekar

    thanx paul it’s very useful…

  • Good!!

  • Naazim

    Just Brilliant!!! Many thanks dude!!! Perfect fix!!!

  • Aosis

    Fantastic! Works like a charm. Thanks a ton…

  • Sohair Ahmad

    what if drop down have submenu, those submenu will not be shown

  • Zia

    Thanks a lot

  • Van Dewey Balao

    Hi Paul, can you help me unhide the scroll bar of my searchbox results. As you can see in my attached screenshot there’s no vertical scrollbar to see the other results. Please help me. https://uploads.disquscdn.com/images/7792b800b7fd62aaeeeb8ed3caa5cf2dea134b47812ecfa03f85883dfde0ea3f.png