Scrollable menus in Bootstrap

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.