Select box replacement 1 (spoiler: it doesn’t work)

My initial thought was “what if I put a bunch of radio buttons and label tags inside a container, then made the container expand, when one of them achieved focus?” This would work as follows:

The radio buttons themselves would be hidden away visually

The label tags would be stacked on top of each other, using position: absolute

The container would also need to be absolutely positioned, so that when it expands, it sits on top of any following content

As soon as one radio button achieved focus, the position of all the label elements within the container would switch to static . As I've also changed their display property earlier to block , they would stack on top of each other, and force the container to take their full, natural height

I did all that, and this happened:

The main issue is that it’s impossible to actually select any value. I assume this is due to the blur event (for want of a better name) occurring before the focus on the label element selects the next radio button.

But there’s another more serious issue. The rule which does most of the work is this:

input:focus ~ label {position: static;}

This reads in English as “If an input element is put into focus, then change all of the following label tags to position: static ". The ~ is a following sibling selector. There is no following and proceeding selector in CSS, and it's unlikely there ever will be, unless you use flexbox. I think this is because CSS thinks in terms of parsing down the DOM, from top to bottom (this is the same reason there's not a parent selector in CSS). It's not as agile as JavaScript, which scampers about the whole tree like a chimp.

Attempt two: the old toggle

The technique of using a checkbox to show and hide content, by using the adjacent sibling selector has been around for a while. My initial thought was to use that to toggle the select box open and closed. It wasn’t ideal — after the user had selected an option inside the select box, the box would not close until they hit the toggle again.

This worked, but I wondered if I could improve it.

I tried the shifting focus trick again: as soon as the user selected an option inside the select box, the focus should shift to that radio button and the select would close. Unfortunately this didn’t work probably for exactly the same reason the first attempt didn’t work: something about focus being lost before the radio button’s state can be changed. I dunno.

Attempt three: all radio buttons, all of the time

What if the select toggle itself was part of the same series of radio buttons as the rest of the select box? This time, the key rule is this:

.radio-toggle:checked ~ .select label {position: static;}

In English, this rule reads “if an element with a class of radio-toggle is checked, find all the following elements with a class of select , then change the position of all the label elements inside it to static ".

This also means that if that same checkbox is no longer checked, the label elements revert to their previous position value of absolute (which stacks them up again). Demo:

I forgot one thing

The original, pure CSS version of this select box replacement has a terrible flaw: it’s only partially accessible via the keyboard. This issue was highlighted to me by the Reddit user jestho, whose one downvote sunk my post from sight.

But of course, jestho is absolutely correct: keyboard navigation is absolutely vital for such controls. I did what I could in pure css, but all I could manage to do is to add an outline around the select box, when it first comes into focus. Unfortunately, once the user selects a value from within the select box, moves onto a different control, then returns to the same select box, the highlight no longer displays (this is because in order to do so, changes on a child element would need to bubble up to the parent element, which ain't going to happen).

And of course, capturing keystrokes and allowing special navigation within an individual select box is completely outside of CSS’s jurisdiction. Which means it’s time for:

Attempt four: pure stylable CSS select boxes, now with added JavaScript!

Most of the JavaScript is there to capture and interpret different keystrokes, such as:

Cursor keys (moving up and down — this mostly comes for free, but the JavaScript stops focus at the end and the start of the range of values)

Page up (moves a configurable distance up, or to the first value, if applicable)

Page down (as above, but down)

Space (opens the select box)

Enter (toggles the select box open and closed)

Escape (closes the select box)

The use of tab is captured by events attached to changes in focus, so I didn’t need to write any jQuery to look out for that.

JavaScript keyboard navigation is a controversial subject, so I’ve attempted to replicate the behaviour of the default (Windows, sorry) select box. For keyboard navigation in other page elements, I’d suggest looking at the WAI-ARIA Authoring Practices, or if you have beef with the W3C for whatever reason, The A11Y Project.

I dislike the hard-coded nature of a lot of this JavaScript (including mine!). Ideally, keyboard navigation should have some kind of solver which has other functions which understands concepts such as first, last, next, previous etc. and can be added to any DOM structure with a few parameters. But that’s a whole other project.

Disadvantages of this technique