BrowserQuirk: Programmatically opening a <select> box.
This one is pretty cut and dry: there is simply no cross-browser solution for programmatically opening up a <select> box.
The Setup
Given a <select> dropdown, I needed to add a custom down arrow. Simple enough.
The custom arrow was part of an icon set font, so I opted to wrap the <select> around a <label> element and add an :after.
My main reason for wrapping a label was because nesting elements such as inputs or selects inside of a label element (or “linking” them with the for attribute) will trigger an automatic focus.
My assumption was that in the case for the <select>, clicking on the label would be the equivalent of clicking on the select menu itself. Since the arrow I implemented is an :after pseudo element, I figured a user click on the arrow would just work towards opening the selectmenu and displaying the options to the user.
But as it turns out, this is NOT the case.
The Journey
Ok, so clicking on the :after of a label did not work.
I started by triggering two events programmatically on the label element — click and change.
Didn’t do anything.
Maybe I could trigger a click directly on the <select> itself? Tried triggering a click on the <select> when a click on the <label> fired.
Still failed.
Maybe :afters and :befores don’t work as expected with a label. I added some text to the label and tried simply clicking that.
Nothing.
Ok, when all else fails, Google.
I hit the Google to find the documentation on labels on MDN. There was nothing substantive there but I noticed that the examples all had input fields. A quick dev tools update and I had the following test:
Behold the initial setup.
Now, on label click, the input field focuses. This is expected.
Now, let’s swap out the code in dev tools (the selected line) with a <select> menu item.
…and let’s see what happens if we click on the “Click me” label now…
As it turns out, logging a click on a label that wraps a <select> will literally just focus on the select menu. It will not open the menu.
Ok, with this knowledge in mind, I hit the google once again for clues on anything that gives more info about this behavior.
The Recovery
As it turns out — this is currently not possible right now. There is a limited possibility to do this in Chrome/Safari/Opera, but Firefox and IE support is glaringly missing. Also, it turns out the ability for the programmatic trigger on Chrome/Safari is due to a bug, as referenced in the wicg thread:
This solution relies on the fact that Chrome currently allows untrusted events to execute the default action…According to the UI Event specification untrusted actions should not execute the default action.
So this brings us back to the original problem: how can we make the custom arrow trigger an open on the select menu?
With #CSSHacks, of course!
There seems to be two possible solutions:
Background-image solution
Use a background image. Here’s the fiddle, referenced in this SO thread. Basically this involves some css background image trickery to get an arrow picture on the right side of a <select> menu.
I couldn’t use this solution because my custom arrow was a font. So, I came up with this hack instead
Box model solution
The main aha! moment came with the realization that the padding of an element counts as a part of the element itself. Meaning, if I were to add a padding-right to my <select> box, a click on that space, even if empty, would count as a click on the <select> box itself.
Now remember, my HTML structure looked kinda like this:
<label>
<select>
...
</select>
</label>
So I added a relative position on my <label> and positioned the :after with my custom arrow positioned absolutely. The space taken up my custom arrow was offset as a padding-right on my <select>. This brought up a z-index issue which I solved by making the background color of my select transparent. This ensured that the select was still on “top” of the custom arrow, but visually, the user can still see the arrow. However, a click by the user on the arrow would actually be a click on the <select> box (since it has a higher z-index inside the <label>). Meaning, a “click” on top of the arrow would display the options in the select menu!
Donezo!
With these changes in place, I had my solution:
And there you have it. Doesn’t exactly solve the issue of programmatically opening a <select> menu, but at the very least we still have good ol’ CSS tricks handy to string together some type of solution.
I should reiterate: hacks on hacks on hacks, yo.