Making custom selects accessible.


[Intro music]

>> MICHAEL BECK: Welcome to technica11y. The webinar series focused on the technical challenges of making the web accessible. Our presenter this month is Gerard Cohen, Lead Accessibility Strategist for Wells Fargo Digital Solutions for Business.

>> MICHAEL BECK: Welcome everybody to this edition of technica11y. I’m Michael Beck, Operations Manager at Tenon, stepping in for our normal host Karl Groves. Before we start with Gerard’s presentation on making custom selects accessible, I would like to mention our next webinar will be on Wednesday, December 5th with Thomas Logan from Equal Entry he’ll be discussing his work on making the Google Song Maker app more accessible. Also, if you missed the last webinar with Nic Steenhout of A11y Rules, you can check it out on our Web site or Technica11yorg.

One final thing before we get started, the Digital Accessibility Legal Summit will be held on December 5th and 6th in Washington, DC, and March 11th and 12th, 2019 in Anaheim. The basic topic will be accessibility lawsuits how to handle them and how to avoid them.

One of the things that stands out for me in regards to this summit is that all of the presenters who are on both sides of the aisle, so to speak, with lawsuits, they would be defendants and plaintiffs, they have been asked to provide something actually tangible that participants with walk away with and not just give some fluff presentation.

It’s really geared towards the C-level executives and legal counsel for companies. I’m not sure if we have any of those in attendance today. But I’m sure everyone knows a person in those positions and can pass the information onto them.

It’s an important event not only for the growth of our industry but also helping to make high-level types who don’t normally think about accessibility, make them aware of the legal risks and obligations that they have towards their users and customers.

The Web site is

And so that brings us to today’s presenter, Gerard Cohen. As noted in the intro, Gerard is the Lead Acessibility Strategist for Wells Fargo Digital Solutions for Business. And today he’ll be telling us how he managed to break one of the myths of accessibility and managed to create a custom select widget that was accessibility. Over to you Gerard

>> GERARD COHEN: All right thank you very much, Michael. Let me go ahead and start sharing my screen here.

Just let me know if you can see that, Mike.


>> GERARD COHEN: Fantastic. Good morning, everyone, happy Wednesday I know you could have chosen to be anywhere else this Wednesday morning so I definitely want to thank you for joining me today.

Let me go ahead and introduce myself for those of you who don’t know me. I’m Gerard Cohen and I am the Lead Accessibility Strategist for Wells Fargo Digital Solutions for Business.

The slides are posted at this Bitly URL that’s

And you can contact or follow me on Twitter.

Today we’re going to be talking about custom selects. Custom selects are kind of the cardinal sins in accessibility, they are right up there with carousels and assistive technology detection. I don’t think there’s a much quicker way to stir up drama in the accessibility community than custom selects. Of course, I’m exaggerating to make a point.

But just to talk about this a little bit more, this is a very recent tweet from Sarah Souoidan: it says us custom select drop-downs are pretty and all but if they are not properly accessible then you should probably not be showing them off. Also nothing beats the usability of a native select UI on mobile no matter how pretty.

So this is the most recent conversation that happened; this conversation happens almost monthly. So why is it such a big deal? Well, I’m going to borrow a recent presentation from Karl actually to help explain this now I have sped this video up for brevity it’s not really necessary to hear exactly what he’s saying but I want you to kind of get the gist of just the amount that he’s talking about this problem. So let’s go ahead and watch this for a brief moment.


>> KARL GROVES: That’s what that does. Anybody here ever make a custom select element? Did you do all of that shit? Did you do all of that shit? Did you do all of that shit? (Chuckles).

>> GERARD COHEN: So the point that was being made there is by Chipmunk Karl is that it takes a lot to provide a usable select widget the problem is nobody ever does all of that stuff. It’s really, really hard to do. So considering it’s such a big no-no, why did I do this? Well I had some problems that I needed to solve. The biggest thing I needed to solve was on iOS the spinner that comes up for select any long values would get truncated and we have to be able to support user generated content that we can’t really control the length of and we also have to display really long account numbers. So, sometimes the only difference in an account number is the last couple of digits if those get can cutoff there’s no way to know what is what. In general, the experience on mobile devices isn’t that great.

Some additional problems, supporting multi-select. The native multi-select takes up way too much space it’s hard to operate with keyboards and actually doesn’t even announce as being able to have multiple selections. Lastly, we needed to provide a better formating for grouped options and of course being able to provide a styled select was nice, too.

Speaking of styling selects: listen, if the only reason you need a custom select is for styling purposes then just take everyone else’s advice and stick to the native it’s actually pretty easy now to style given just a little bit of CSS. Now, I can’t remember where I heard this but for the longest time I had a really long recollection and believe the reason why we can’t style select today is because it’s the host OS that’s providing the select. I remember reading that some time ago…I can’t find proof for it ,but, it’s something I learned very early on and I could be wrong but it makes sense if you consider the way selects are the only components that are allowed to spill outside of the browser window.

So let’s talk about the official ARIA authoring practice for listbox because this is the first place usually people will go. First of all, it just doesn’t work everywhere. And there are a few notes in the documentation that provide an explanation for this.

I’ll read a few of them. The first one: “Because the purpose of this guide is to illustrate appropriate use of ARIA 1.1 as defined in the ARIA specification these design patterns, reference examples, and sample code intentionally do not describe and implement coding techniques for working around problems caused by gaps in support for ARIA 1.1 in browsers and assistive technologies.”

“Except in cases where the ARIA working group and other contributors have overlooked an error, examples in this guide do not function well in a particular browser or with a specific assistive technology are demonstrating browser or assistive technology bugs. Browser and assistive technology developers can thus utilize code in this guide to help assess the quality of their support for ARIA 1.1.” What they are saying is first of all they don’t provide any guidance as to how to work around issues you may discover in particular browsers or assistive technologies because they are really a guide on ARIA 1.1 they have to stay pretty close to the roots there.

And the guides also — these guides provide the state at which they would like things to be supported. So it’s kind of like a best wish.

It’s basically at a certain point a testing tool for browsers and AT vendors to help beef up support if something is not working. There’s actually one more note from the documentation I want to pull out.

“Currently this guide does not indicate which examples are compatible with mobile browsers or touch interfaces. While some of the examples include specific features that enhance mobile and touch support, some ARIA features are not supported in any mobile browser. In addition, there is not yet a standardized approach for providing touch interactions that work across mobile browsers.”

So here there are basically acknowledging that the patterns don’t support mobile browsers at this time. So, that’s a problem for the issues that I was trying to solve.

So another big issue for me is that the ARIA pattern itself does not actually function as a form element. There’s no data being submitted in a traditional form sense. You need to figure out your own way to get the selection value and pass it along with your form. Along with that, there’s no guidance on how do you make it required, how do you perform validation, how do you mark something as invalid. Those ARIA states aren’t allowed on buttons, most of them aren’t.

Lastly, they don’t have any guidance on opt groups how do you group related items, so, these are some of the gaps of functionality that are just not addressed there.

So before I pull the curtains back I have to admit that some of this — it’s a little unconventional and definitely the first rule of ARIA to not use ARIA if you can use native elements has been thrown out and I’m heading into a bit of unchartered territories, but, I can assure you that the end user experience is well worth it. Still if you don’t want to see the monstrosity now is your chance to opt out so you have the option of taking a red pill or blue pill.

So I guess everyone is going to take the red pill, so, we’ll just go down the rabbit hole. So let’s talk about the major players here.

I’m going to be demonstrating code using plain old web technologies. So that’s just HTML, CSS and vanilla JavaScript. I wanted to be able to demonstrate everything without forcing you guys to be human JS compilers, but, honestly the real star of this is the HTML anyways.

I’ll be starting with native select because I like progressive enhancement. I think it’s still a worthy cause. I moved to the suburbs where Internet connectivity is spotty; my browsing experience on my iPhone is pretty crappy. It’s also bad when taking the train into the city. I don’t want to sound like get off my lawn guy, but I don’t have time for brochureware type site to download and rendered entirely in JavaScript. It’s just not for me anymore. Some elements that are going to be used for this are the simple — a simple span for the trigger and unordered list for the options and, of course, a lot of ARIA.

So the inspiration for this implementation of custom select came from a couple of places. First was actually the ARIA pattern we talked about earlier. From there additional interactions were borrowed from native select instances on Windows and Mac browsers mainly IE, Firefox, and Safari. The goal was to try to build something as close to a native select as possible. In most cases, there’s a pretty close mapping, but, in some cases, the experience is augmented to add additional features that were missing so I started where we are right now and tried to make it better where possible.

Let’s talk about the keyboard interactions I’ll be implementing first of all on the select trigger: space, up, or down will display the list.

Focus will move to the unordered list. And an interesting thing that I discovered was difference between Mac and Windows in this case. Native selects on Windows and Internet Explorer will actually let you navigate the items without having to expand the list just by using the arrow keys and in my implementation I prefer to display the list so everything can be seen by sighted users and you can navigate without having to make a selection.

On the actual options list, up and down is used to navigate the items but they don’t actually cycle. Navigating does not make a selection and we’ll talk about selection in a second.

Home will move to the first item in the list. End moves to the last item in the list. There’s a type ahead functionality where basically typing a character will move focus to the item that starts with that character. Space will select the item, close the list, and then focus on the trigger.

And escape will close the list and focus on a trigger without making a selection.

So,, speaking of selection the ARIA pattern has a notice about whether or not selection should follow focus. That is, should the value of the selection be updated as you’re navigating along the options, which is how the native selects on Windows work.

I thought it was — this — since this happens an Windows you could stick with that but I chose to make the selection a specific interaction requiring a space key press for selection.

Another issue is again on Windows, when you’re pressing escape, it closes the drop-down and the last item you had focus is your selection. Again, the selection was following focus and this just doesn’t feel right, there’s no way to back out of a selection

But if you feel that it’s important for you to maintain this, then please go for it.

Lastly, I want to point out in Safari on a Mac, when a list is open tab is essentially trapped and on Windows tab will close the list and return focus back to the drop-down trigger. I liked that behavior so I kept that in my implementation, as well.

So of course we can’t leave out mouse and touch interactions; these are actually pretty simple. Basically tap or click — tap or click to open and tap or click to select. And for assistive technologies using touch, swiping would allow you to navigate the items.

So let’s talk about some of the things I discovered before I actually show you the demo. There’s nothing really super fancy about the JavaScript, to be honest. It’s really boring. It’s just a bunch of event handlers setting properties of handling focus. Again, like I said the star is actually in the markup because that’s what the browser is interacting with, that’s what the user is interacting with, and what assistive technologies are interacting with, so JavaScript doesn’t really play a big part in the entire process. The code is actually pretty procedural and boring to be honest, but, I wanted to point out some things that I discovered in this journey.

So the role of combobox versus button. The ARIA Authoring Practices show examples of using a button for the select trigger and I can assume they did this because role of combobox by definition is a composite widget and it consists of a text input field and a list and despite this I end up choosing the role of combobox for a couple of reasons.

The first one is that Windows announces native selects as comboboxes and VoiceOver just announces it as a popup button, so there’s a match there with at least the role that’s being announced.

Another big thing for me was that combobox is actually recognized as a form element, so if an assistive technology user is trying to discover elements by type, say bringing up examples of elements on the page, the combobox element will show up but won’t show up as a form element and may not be obvious to look there for that particular role.

Lastly, as I mentioned, — well actually because of the combobox role unfortunately one thing I found was there are certain automated tests that will complain because there’s no text box child so just be aware that certain automated tests will actually call that out on you.

Another big issue is again I’m trying to make sure this stays as a form element and ARIA-required is a property that is not allowed on buttons. So, this makes it less of a form element. You could get around this by just adding the word required to the label. But again for me it was important that the role was listed as a form element. So this is why I chose to go with combobox even though it’s not a pure combobox since it’s missing that text input field component.

So one thing that I was kind of surprised about was that the ARIA-selected state wasn’t announced everywhere. While navigating lists, there’s no notification that an item was selected or not. Of the three screen readers I tested VoiceOver was the best by announcing the number of items selected and actually it will announce the items selected upon opening the list.

JAWS 2018 and IE11 announces the selected item upon first announcing the list. But it’s kind of a strange announcement. So if you selected dog it would just announce list dog it wouldn’t say dog selected or anything like that.

And on Windows 7 with the NVDA and Firefox, it doesn’t announce anything at all, so it’s possible you’re expected to remember that selection from when you opened the trigger but that doesn’t seem right to me so again I just — what I did here was I added a string of comma space selected to the ARIA-label for the option.

Another thing was that Firefox for whatever reason wasn’t announcing the content of the combobox. And that was kind of a big deal, because it just announced the role and the expanded say but not a value. And this happened in Firefox with both NVDA and JAWS but not in IE so I feel pretty confident it’s just an issue with Firefox. Except when I got to testing the Windows 10, it was [an issue]. So I’m not really sure what’s going on there.

In any case, again, I got around this by using my favorite fix. I just forced the content using ARIA label on the content box to use the field name and selected item to be announced and unfortunately this had a negative impact in that VoiceOver now announced that content twice which is kind of unfortunate but I have a thing where two announcements is better than no announcement, so I had to make sure that that case was covered.

One thing about setting ARIA labels, especially on these interactive controls obviously, is you want to make sure that that ARIA label is matching the visual label or at least make sure what’s visible is at the start of the ARIA label and this is to help support users using voice technologies.

So ARIA described by gave me a few problems for the groups I’ll show you in a little bit. It appears as though ARIA described by wasn’t looking at all on anything I tested. I tried with both NVDA and JAWS and got nothing; it worked fine with Firefox on IE11 with Windows 7 and 10. I’ll probably log a defect for that unless someone on the phone already knows something about that.

ARIA-described by on iOS and Safari did announce the ARIA described by info but there was a brief pause which made it weird if you’re not used to waiting for it but it did announce. It wasn’t really long, like the seven seconds it used to be on Mac OS, but it was probably a second and a half. It was a noticeable delay but it does announce. JAWS and IE announce the ARIA described by but the instructions on how to use the JAWS shortcut to announce the extra content was announced first every single time and then the extra content would announce automatically.

So I had an instance where I had tutor messages on and that turned it off but then in another instance I turned it off they were still announcing so I’m not sure if there was something going on there with my particular instance of JAWS.

Some things for mobile. Something — it’s super important that you render the list right after the trigger. Otherwise swipes won’t enter into the list properly: this is really important for VoiceOver on Mac and iOS as well, so it’s just generally good guidance to have. Another tip is to listen for focus events on the document. This way you’ll be able to hide the list if a user swipes out of the list or somehow focuses on another item on the page. For mobile listening to the focus depends on the document. You can close the list and that way it won’t stay open.

One difference about VoiceOver on iOS was that even though I was programmatically setting focus to the actual list once it was opened, VoiceOver focus stayed on the combobox on iOS. So that was just a different interaction there that I wasn’t expecting

And because of the extra ARIA labels that I needed to add to smooth out all of the other issues, unfortunately again, the combobox and the selected states would announce twice.

So form controls: I wanted to maintain that again with our custom widget. And the way I did this is I actually kept the native select under the hood.

And as selections are made with the custom widget, they are synced to the native select that was hidden.

And this way values are still submitted or can be serialized like normal form elements. And it would also allow you to do some pretty simple validation. You would basically validate the native select as you normally would and then sync the validity of that to the custom select and present an error. I didn’t actually work that out in my code example but it’s easy enough to do anyways.

A couple of comments on the actual design on the user interaction of it. Just some recommendations that I would recommend is trying to prevent overscroll. So when the list is open, you want to set overflow hidden on the body. It’s really annoying on devices specifically when you’re scrolling through a list that has a lot of options and then you get to the bottom unknowingly and then you scroll past it, because the page itself starts to scroll. And that also happens just if you’re scrolling with a mouse on a desktop or laptop, as well.

So setting overflow hidden on the body when the list is open will help prevent that and it’s just a nice thing for your users there.

Another thing is list positioning and this was something that I didn’t implement in the code that I’m about to show you. But OSs do this really nice thing where the selected item will actually appear in the same place as the label of the select, as well. So, if you can imagine you have a really long list. So, for example a list of states which would be at least fifty. If you had an option down at the bottom selected and you opened that list, if it didn’t change the positioning for you, then you would — you probably wouldn’t realize that an — wouldn’t realize the option at the bottom of the list that was hidden from view — you would need to scroll to it — you probably wouldn’t know it was selected so this is just a nice thing that browsers will do for you and I highly recommend that you do that, as well.

Another thing was that I added a check mark to the selected option versus just changing a background color. I just felt like this felt better versus just relying on too dangerously close to using color to designate a selected item. So I added a check mark there to designate.

Okay. So now we’re going to get into some demos. And these are some videos that I have prerecorded demonstrating on different browsers and screen readers.

First up I want to show NVDA and Firefox on Windows 10.

>> COMPUTERIZED VOICE: Custom select Mozilla Firefox. Custom. Cities. Choose a favorite animal.

Favorite animal combobox collapsed required clickable.

Space Expanded. Favorite animal options list. Dog not selected 1 of 6. Fish not selected 2 of 6. Horse not selected 3 of 6. Bird not selected 4 of 6. Ferret not selected 5 of 6. Cat not selected 6 of 6. F. Fish not selected 2 of 6. F. Ferret not selected 5 of 6. Space. Ferret favorite animal combobox collapsed required.

Space Expanded. Favorite animal options list. Dog not selected 1 of 6. Ferret selected 5 of 6. H. Horse not selected 3 of 6. Space. Horse favorite animal combobox collapsed required.

Please select cities combobox collapsed. Space. Expanded. Cities options multiple selections available list. Los Angeles not selected California 1 of 8. San Francisco not selected California 2 of 8. Space. San Francisco selected California 2 of 8. Oakland not selected. Space. Oakland selected California. Roseville not selected California Houston not selected Texas 5 of 8. Space.

Houston selected Texas 5 of 8 Austin not selected Texas space. Austin. Muleshoe not selected Texas 7 of L. Los Angeles not — Space. Los Angeles selected California 1 of 8. Los Angeles, San Francisco, Oakland, Houston, Austin, cities combobox collapsed. Space. Expanded. Cities options. Multiple selections available list. Los Angeles. Selected California 1 of 8. Space. Los Angeles not selected California 1 of 8. A, Austin selected Texas 6 of 8. Space. Austin not selected Texas 6 of 8. San Francisco —

>> GERARD COHEN: So a couple of things that you’ll notice there was that obviously the keyboarded actions were in place but there was a lot of good information as far as selected states. Another thing I’m not sure if you noticed you’ll maybe notice in the next couple of videos that I show but one behavior that I picked out from native selects was that in grouped options the group label itself is not actually navigable or selectable. And so I carried that over onto this widget, which was really nice because you notice as the — as NVDA was listing the item count, it wasn’t counting those group labels. So that was a really nice behavior that I was able to add in there.

So next I’ll show you VoiceOver and I want you to notice how you have in this case you have the double announcements on the combobox trigger and on the selected state and again this was because of the extra ARIA labels that I needed to add.

>> COMPUTERIZED VOICE: Welcome to Mac OS VoiceOver is on Safari new tab button vertical splitter custom select web content.

Main, main. Heading Level 1 custom select. Favorite animal. Choose a favorite animal choose a favorite animal favorite animal required combobox. Zero items selected. In favorite animal box list items selected dog text 1 of 6. Fish text 2 of 6. Horse text 3 of 6. Bird text 4 of 6. Ferret text 5 of 6. Not cat text 6 of 6. Fish text 2 of 6. Ferret text 5 of 6. Ferret, ferret, favorite animal required combobox main. Please select please select cities combobox. Cities options multiple selections available list box zero items selected in cities options multiple selections available list box zero items selected Los Angeles, California 1 of 8 San Francisco, California 2 of 8 Oakland, California 3 of 8. Roseville California 4 of 8. Houston, Texas 5 of 8. Texas Houston selected 5 of 8. Roseville California 4 of 8. Added to selection two items selected. Oakland, California 3 of 8. Added to selection three items selected. San Francisco, California 2 of 8. Added to selection four items selected.

California San Francisco selected. Elgin Texas 8 of 8 added to selection five items selected.

Texas Elgin selected you are currently on an escape button San Francisco Oakland Roseville Houston Elgin. San Francisco Houston Elgin cities combobox main. You are currently on a combobox cities options multiple actions available. California San Francisco selected 2 of 8. California Oakland selected 3 of 8. California Roseville selected 4 of 8. Texas Houston selected 5 of 8. Texas Elgin selected 8 of 8. San Francisco Oakland Roseville Houston Elgin San Francisco cities combobox main. San Francisco Oakland Roseville Houston Elgin San Francisco cities combobox main.

>> GERARD COHEN: You noticed some of those duplicates announcements there and also added additional information upon navigating the list it would immediately tell you the items that were selected already which I thought was kind of a nice feature that the other assistive technologies didn’t do.

That was built in from the specific roles I was using VoiceOver was doing that. I didn’t do anything extra for that. It did sound a little long but some users may find that helpful.

Lastly real quick if you’ll bear with me I want to show you what this is actually like on VoiceOver with iOS, as well.

>> COMPUTERIZED VOICE: VoiceOver on. Custom select. Heading Level 1. Main. Favorite animal. Choose a favorite animal. Favorite animal. Choose a favorite animal. Shows Popup. Double tap choose a favorite animal. Favorite animal. Choose a favorite animal. Shows popup. Dog, list starred. Fish. Horse. Bird. Ferret. Not cat.

List end.

Ferret, bird, horse, horse. Favorite animal. Horse. Shows popup. Horse Favorite animal. Horse. Shows popup. Dog. List start.

Fish. Selected. Horse. Selected. Bird. Bird. Favorite animal. Bird. Shows popup. Cities. Please select cities. Please select. Shows popup. Please select. Cities. Please select. Shows popup. Los Angeles List start. Cali — San Francisco. California. Oakland. California. Selected. Oakland selected. Roseville. Houston. Custom select. Heading level — Houston. Austin. Selected. Austin. Selected. Muleshoe. Elgin. List end. Back button.

Bird, favorite animal — bird, favorite animal.

Bird. Shows popup.

>> GERARD COHEN: So I guess maybe VoiceOver hasn’t been in Texas because it had a problem pronouncing Muleshoe and Elgin and actually I have more videos using other browsers and assistive technologies, those were just kind of the highlights so if you’re interested, let me know, I can share them.

Okay. If you’re still with me, let’s take a look at some code. The actual example that I just demonstrated in the videos is available online. You can go to

Okay. First thing I want to show you is the markup — the initial markup that’s used for the native select. This is before any of the JavaScript has kicked in. And it’s just your plain select markup.

You have a label there. And then you have the select with the options. And there’s a div wrapper around the select. And it’s important in my case to keep the DOM in this order with these classes because the class names are the same being used for the custom select widget. So what happens is the — the default state before JavaScript and after when JavaScript has rendered everything looks exactly the same, so that way you really can’t tell the difference and so you actually navigate the select itself, that you’re using custom versus select, a native select.

So this is what you start off with. Again you’ll notice on the select I have required on there. And that gets translated to the custom select as well. I’m not sure if you noticed the first — the favorite animal select was announcing as required.

So you can do validations on that.

And so once the JavaScript kicks in this is what it turns into.

So just a little bit more code here. Markup. So again we start off with a span instead of a label for the actual label itself. And in my experience, using an actual label element obviously there’s not a real form there. You could maybe attach it to the hidden. But I had problems with older — with IE11 and JAWS it would announce that — it would double announce that label and it would also associate that label with other elements. Really, really weird behavior.

So that’s the visible label. Then, the big deal with the combobox role. So it’s just a span. It has a tabindex of 0 to make it focusable. It has the role of combobox. It ARIA auto complete as none that’s just to be a little bit more pure to the actual combobox role and make sure there’s certain assistive technologies or even maybe some automated testing didn’t — it wouldn’t complain that that value wasn’t on there. Obviously I’m maintaining the ARIA expanded state. And then here I’m adding the ARIA label, which is in the initial rendering, it’s the placeholder which would be choose a favorite animal comma and then favorite animal which is the actual label of the field. And the comma is in there to add a nice pause between the two pieces. The assistive technologies will treat that comma as a pause, so it makes it a more human readable or more human sounding announcement. Then I have ARIA owns which actually references the ID of the ordered list itself. And then, finally the ARIA required which I pulled off of the native select required attribute translated to ARIA required.

Then I had to use a data attribute to store the actual placeholder text. That way I can reference back to it when — if you deselect the option in some cases, especially with the multi-selects, it’s nice to be able to replace that content back to the placeholder.

Moving to the actual list itself. It’s just a UL with the role of list box. And in this case I added the ARIA label there to say favorite animal options. And that way you have some context there when you first encountered the list. It would announce favorite animal options. It also serves the purpose for mixed select boxes. Add additional content there that says multiple selections allowed. And that’s information from the — information that’s not normally conveyed to assistive technology with a normal multi-select so I felt it was important to provide that information to a user. That way they know they can select more than one option.

Moving into the actual list items themselves. Tabindex of negative 1 with the role of option and ARIA selected equals false is the default state for all of these unless you already have one that’s selected. And this was important. When my — like for maybe 75% of my testing I was only adding ARIA selected equals true and removing ARIA selected: it’s just a thing I like to do. It wasn’t until I started testing on Windows 10 that I realized I think it was Edge required the ARIA selected equals false otherwise if ARIA selected equals false wasn’t on the option it would announce it as selected. So ARIA selected equals false — basically ARIA selected has to have a proper value for each one of the options later on I actually found that in the actual ARIA authoring pattern. It does so to have ARIA select equals false on there. Then underneath that you’ll notice I have the actual native select. It’s visually hidden it has ARIA hidden equals true so hidden from sighted users and also hidden from assistive technologies and a tabindex of -1 to make sure you didn’t inadvertently tab to it even though it’s hidden that’s just a cloned version of the original select I started with this is the select I manipulate based on the selections of the custom select I would sync them back down to the native select so when the form is submitted, the selected values of that select get passed along with the form. Also, again, you can do validation based off of this native select that’s hidden behind the scenes or you could serialize the form and send the values off via any other method that you’re using to submit the form.

So really not that exciting here. This is kind of just standard in my opinion. The real fancy stuff the stuff that I got excited about came with the actual grouped options now. This is stuff that basically I had to come up with on my own. Based on a lot of testing, this is how I got the options to be announced along with basically the parent level that they were grouped under.

So again just starting with the same unordered list with the role of list box, I have my ARIA label there that references the label of the field. So, in this case, it was cities and then I added options to it and then I add the additional context to let the user know that multiple selections were available. ARIA multi-selectable is set to true. Even though that’s not announced by assistive technologies, I still have it on there. Then of course tabindex of -1 is on there because I’m managing focus entirely myself.

Then after that, the list items so treating the list items a little bit differently. The actual group labels don’t have a role of option. In fact, I wanted to be very, very kind of heavy handed with — did not want them to be announced, so I added a role of presentation and ARIA hidden equals true. Also they are not navigable by keyboard so they operate the same as a native group selects there in that case. For the actual list options, still the same — pretty much the same, tabindex equals -1, you have a row of option. And in addition here, this is where I had the ARIA described by that will reference the ID of the — basically the group label, the option above there.

And then again ARIA selected equals false. And that’s how I was able to get — it’s actually pretty simple except for Edge where ARIA describe by was announcing. That’s how I was able to associate the — in this case these two names with the other names and I was surprised how it worked with most of the assistive technologies, it picked it up right after it. A lot of other examples I saw out on the Internet used nested lists. And in some cases they didn’t work, but, honestly I didn’t really like the interaction there because of the way it would list levels and additional information that I just didn’t feel was really necessary. And this was a much simpler more elegant solution.

And as I mentioned before, the JavaScript, there’s really nothing special to the JavaScript. When you look at the code, it’s very procedural. And it’s very — I was very explicit with everything in these examples because I wanted you to understand exactly everything that was going on, every little step, every little property that I updated every time I moved focus, I wanted it to be very, very explicit so that you can see that. Obviously it can be refactored. It’s not production code; like, this is not a UI library widget or anything like that it’s literally just to show the example of everything so it’s actually pretty boring. But one thing I did kind of geek out on was the code I’m using to perform the type of ahead. And a lot of examples I saw there was a lot of — there was just a lot of code that went into that. It is kind of difficult process if you consider that it has to be able to cycle through all of the options. So I’m going to show you that real quick.

And this is where obviously ES6 was really handy because of a lot of really cool methods for arrays.

So the first thing that I’m doing here is, you’ll notice I have basically a variable named options and that is literally the options in the list. So the first thing I do is using the sum method which basically returns if there’s a match for a particular value. So in this case, the very first thing I’m doing is I’m just making sure that at least one item starts with the key that you just pressed. If not, it’s just an easy out. It will exit immediately versus going through the rest.

This is probably the only case where I did a little bit of optimization on the code. Everything else again was very procedural and I wasn’t following engineering best practices for performance and all of that stuff because I wanted it to be explicit. This is the only case where I started to do a little bit of optimization. I don’t know why. I just felt like I needed to do it. Working with arrays is just always — stresses me out.

If I meet that condition that okay, there’s at least one option that starts with the letter — the key that you pressed, first thing that I’m doing is I’m checking to see if there’s an item that’s already focused so I’m saving that in a variable here called focus. And that’s because I need to know which index on the array options to start searching from. So when you’re doing a type ahead, I don’t know if you saw in the videos, but for example in the animals list I had two animals that started with F. There was fish and there was ferret. So if fish is already focused. My next press of F should start searching from that item and find the next one underneath it, which was ferret. And then on ferret if I pressed F again it should cycle back up and find fish.

So I needed to first find out if there was one focused already. And if there was one — if there wasn’t one that was focused then I knew I had to start from the top of the list. And that’s in — I don’t know if you can see. It’s basically Line 402 I’m using the find index method to search through the options starting from the top. And then return a value back rather quickly. If there was an item that was already focused, and this was the part that I geeked out on, is basically what I did was created a new array starting with splicing from the index that was already focused so that would — for example if index 3 out of 5 was selected on this, I would slice out items 3 through 5, or actually 4 through 5, first and stick that at the top of the list. And then I would splice out the front half and add that to the end. I’m not sure if I’m explaining that properly. But what that means is that now instead of having a loop through the array twice or multiple times to find out, “Okay I’m at the bottom of the list,” I didn’t find anything, I have to start at the top, what happens is it’s just one flat list to search. And it will automatically produce the result of loop — starting over from the top.

So I’m using slices to do that And then finally, again, just using index of and a couple — and find, which are just other really cool ES6 array methods to find the item in the list and finally just end up focusing it.

So it was pretty surprising with these few lines I was able to perform that function.

>> CYPHER: I know what you’re thinking because right now I’m thinking the same thing actually I’ve been thinking it ever since I got here. Why, oh, why, didn’t I take the blue pill?

>> GERARD COHEN: All right. So I know that was a lot of information so thanks for sticking with me. I’m hoping that you got something out of it. As I mentioned earlier, it may have been a little unconventional and you may think all of that wasn’t worth it but I had an opportunity to improve the experience for our users and that made it worth the effort for me. At the end of the day, you know, custom selects: they are not going away. I know sometimes it’s easier to just say no to something than to fully explain it and flesh it out but I don’t think telling designers and developers not to build custom selects is doing anything but making accessibility worse so I’m hoping you can all take everything I’ve shown you here we can crowdsource make it better for everyone so hopefully one day building custom selects won’t be such a bad thing one last thing we have a few — a few minutes for some questions probably but one last thing I wanted to send a shoutout to my team who helped me test a lot of this stuff so Richard for his JAWS help and Michelle Little for her NVDA help #oxsquad. Yeah just thank you for joining me today thank you for Karl and Michael and everyone for attending and allowing me to share this stuff, thanks to ACS Captions. My name is Gerard, you can catch me on Twitter. If you know any developers that want to learn how to write accessible code, I have a course on Perl that they can check out and we have time for questions.

>> MICHAEL BECK: Yeah, thank you Gerard very much. We have a few questions. One is, you may have answered this whenever you pulled the code up, but it’s from Perry: if the input is backed by a select element, how are you submitting multiple values?

>> GERARD COHEN: Yeah, actually having that native select with multiple on the select, I mean everything was just handled for me that was one of the nice things to make sure I had the native select under the hood there.

>> MICHAEL BECK: Then PJ asked are there any considerations if the list has to be translated?

>> GERARD COHEN: I didn’t get into that. Yeah, I didn’t get into that. That’s not something that I thought about. But I mean —

>> MICHAEL BECK: The answer is no.


>> MICHAEL BECK: Anyone else have any other questions? I really don’t see much. So thank you all very much for attending. And like I said, the next one will be on December 5th with Thomas Logan. And spread the word for the — oh, geez, spread the word for the webinar. Let everyone know if you enjoyed it. And we will see you next month. Thanks, Gerard.

>> GERARD COHEN: Thanks, everyone, take care

Gerard Cohen

About Gerard Cohen

“Do you ask a dolphin how it swims, or an eagle how it flies? No, because that’s what they were made to do!”

Gerard K. Cohen loves front end engineering so much that he is on a mission to make sure that the web is inclusive to all users, making rich internet experiences available for all. He believes a great user experience includes performance and accessibility.

Gerard lives in Oakland with his wife and Betta fish, Squiggles, and when he is not sleeping or drinking Zombies at tiki bars, he helps raise awareness by speaking at Front End and Accessibility conferences around the country. He is also the author of “Meeting Web Accessibility Guidelines” on Pluralsight.