Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for JavaScript and Accessibility: Accordions
Lindsey Kopacz
Lindsey Kopacz

Posted on • Edited on • Originally published ata11ywithlindsey.com

     

JavaScript and Accessibility: Accordions

Originally posted onwww.a11ywithlindsey.com.

When I first wrote my post aboutJavaScript and Accessibility, I promised I would make it a series. I've decided to use mypatreon to have votes on what my next blog post is. This topic won, and I'm finally getting more time to write about JavaScript!

So this topic I am going to go into a deep dive on how to make accordions accessible! Our focus is:

  • Accessing the accordion with a keyboard
  • Screen reader support

HTML Structure

I did a few pieces of research about the HTML structure. I read thea11y project's link toScott O'Hara's Accordion code. I also readDon's take about aria-controls - TL;DR he thinks they're poop. I couldn't escape reading theWAI-ARIA Accordion example as they set a lot of the standards. My hope is with all the information about what's ideal, I can help talk through why everything is important here. It's easy to get overwhelmed, and I'm here to help!

So if you read my post3 Simple Tips to Improve Keyboard Accessibility, you may recall my love for semantic HTML.

If you need JavaScript for accessibility, semantic HTML makes your job significantly easier.

Many of the examples I found use semantic button elements for the accordion headings. Then the examples used div tags as siblings. Below is how my code starts:

Adding the ARIA attributes

I wrote that ARIA is not a replacement for semantic HTML in aprevious post. New HTML features that come out are replacing ARIA all the time. In an ideal world, I would use thedetails element. Unfortunately, according to theBrowser Compatibility Section, there is no support for Edge and IE11. Until browser support improves, I'll be sticking to the "old fashioned" way of doing it. I'll be adding ARIA for the context we need. I'm looking forward to seeing the compatibility expand to Edge!

First, I am going to add somearia-hidden attributes to the div to indicate thestate of the accordion content. If the collapsed element isclosed, we want to hide that content from the screen reader. Can you imagine how annoying it would be to read through the content you are not interested in?

- <div>+ <div aria-hidden="true">......- <div>+ <div aria-hidden="true">......- <div>+ <div aria-hidden="true">

The next thing we do is ensure that we have anaria-expanded attribute to the button. When we are on the button, it tells us if something is expanded or collapsed.

- <button>+ <button aria-expanded="false">......- <button>+ <button aria-expanded="false">......- <button>+ <button aria-expanded="false">

When it comes to ARIA for me, less is more. I am going to leave it at that and use JavaScript in a future section to toggle the states of the ARIA attributes.

Adding Some Styling

I'm not going to focus too much on the CSS specifics. If you need a CSS resource, Ali Spittel's postCSS: From Zero to Hero and Emma Wedekind'sCSS Specificity post are great.

First, I add classes to the divs and the buttons for good measure.

- <button aria-expanded="false">+ <button aria-expanded="false">    Section 1  </button>- <div aria-hidden="true">+ <div aria-hidden="true">

Then I add a bunch of styling to the buttons. I wrote this CodePen with SCSS.

(Quick note: for the triangles on the iframe, I used theCSS Triangle article from CSS tricks.)

I want to point outexplicitly this code:

.accordion{// previous styling&__button.expanded{background:$purple;color:$lavendar;}}

I want to specify what the button looks like when it was open. I like how it draws your eye and attention to the open section. Now that I see what they generally look like, I am going to add the styling to collapse them. Additionally, I'm adding some open styling.

  &__section {    border-left: 1px solid $purple;    border-right: 1px solid $purple;    padding: 1rem;    background: $lavendar;+   max-height: 0vh;+   overflow: hidden;+   padding: 0;  }+ &__section.open {+   max-height: 100vh;+   overflow: auto;+   padding: 1.25em;+   visibility: visible;+ }

Finally, let's add some focus and hover styling for the buttons:

  $purple: #6505cc;+ $dark-purple: #310363;  $lavendar: #eedbff;
  &__button {    position: relative;    display: block;    padding: 0.5rem 1rem;    width: 100%;    text-align: left;    border: none;    color: $purple;    font-size: 1rem;    background: $lavendar;+   &:focus,+   &:hover {+     background: $dark-purple;+     color: $lavendar;++     &::after {+       border-top-color: $lavendar;+     }+   }

A quick note: you could likely add styling by adding.accordion__button[aria-expanded="true"] or.accordion__section[aria-hidden="false"]. However, it is my personal preference using classes for styling and not attributes. Different strokes for different folks!

JavaScript toggling

Let's now get to the fun part of toggling the accordion in an accessible way. First, I want to grab all the.section__button elements.

constaccordionButtons=document.querySelectorAll('.accordion__button')

Then I want to step through every element of the HTML collection that JavaScript returns.

accordionButtons.forEach(button=>console.log(button))// returns <button aria-expanded="false">//    Section 1//  </button>//  <button aria-expanded="false">//    Section 2//  </button>//  <button aria-expanded="false">//    Section 3//  </button>

Then for each of those items, I want to toggle the class for the opening and closing for visual styling purposes. If you remember the.open and.expanded classes that we added before, here is where we toggle them. I am going to use the number in the ids that match up with each other to get the corresponding section for that button.

accordionButtons.forEach(button=>{// This gets the number for the class.// e.g. would be "1"constnumber=button.getAttribute('id').split('-').pop()// This gets the matching ID. e.g. the// section that is underneath the buttonconstassociatedSection=document.getElementById(`accordion-section-${number}`)})

Now we have the current valuebutton in the callback and the associated section. Now we can get to toggling classes!

button.addEventListener('click',()=>{button.classList.toggle('expanded')associatedSection.classList.toggle('open')})

Toggling classes is not all we want to do. We also want to toggle the aria attributes. From the previous section, aria attributes communicatestate to screen readers. Changing the classes shows what happened to a visual user, not to a screen reader. Next, I check if the button contains the class in one of those elements. If it does, I'll swap the state for thearia-hidden andaria-expanded.

button.addEventListener('click', () => {  button.classList.toggle('expanded')  associatedSection.classList.toggle('open')+ if (button.classList.contains('expanded')) {+   console.log('open?')+ }})

The conditional fires after we set the classes, and if the class has expanded, it is open! So this is where we want to use the states and communicate it's open.

button.addEventListener('click',()=>{button.classList.toggle('expanded')associatedSection.classList.toggle('open')if(button.classList.contains('expanded')){button.setAttribute('aria-expanded',true)associatedSection.setAttribute('aria-hidden',false)}else{button.setAttribute('aria-expanded',false)associatedSection.setAttribute('aria-hidden',true)}})

Now we can open and close the accordion with the spacebar or the enter key!

When I go through the accordions headers without opening them, they do not read them in the section. That's a good thing! When I open it, I'm able to read it.

Neil Patrick Harris sitting in the driver's seat of a car, making a large grin. Then he nods his head and puts his thumb up.

Progressive Enhancement

Now, I know how much we all rely on JavaScript loading, particularly with all the frameworks we use. Now that we know the functionality, let's refactor the code a bit. The goal is to ensure anyone can access the accordion if JavaScript is not enabled or the user has connectivity issues.

My final touch is to

  • Keep all the accordion sections open by default (Adding an.open class to the HTML sections)
  • Remove the 'open' class once the JavaScript loads.
  • Add all the aria attributes with JavaScript and remove that from the HTML

I want to removearia-expanded="false" andaria-hidden="true" from my buttons and sections, respectively. I also want to add theopen class to the html, so it's visually open by default.

- <button aria-expanded="false">+ <button>    Section 1  </button>- <div aria-hidden="true">+ <div>

I want to set those attributes and remove that class in the forEach loop ofaccordionButtons.

accordionButtons.forEach(button => {+ button.setAttribute('aria-expanded', false);  const expanded = button.getAttribute('aria-expanded');

Then I want to create anaccordionsSections variable and do two things:

  • set thearia-hidden attribute
  • remove the.open class.
constaccordionSections=document.querySelectorAll('.accordion__section');accordionSections.forEach(section=>{section.setAttribute('aria-hidden',true)section.classList.remove('open')})

We're done! Remember, we haven't removed any of the other code or event listeners. We are just adding all those attributes in with JavaScript.

Conclusion

What did you think of this post? Did it help you? Are you excited for the<details> element? Let me know onTwitter what you think! Also, I now have apatreon! If you like my work, consider becoming a patron. You’ll be able to vote on future blog posts if you make a $5 pledge or higher! Cheers! Have a great week!

Top comments(12)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
angeliquejw profile image
Angelique
solving problems while writing smarter code + teaching others to do the same // pronouns: she/hers
  • Location
    Baltimore, MD
  • Work
    Engineering Manager
  • Joined

However, it is my personal preference using classes for styling and not attributes.

One of the wins of using ARIA attributes to style interactive components like this is that the component willnever workwithout the ARIA changes baked in. This may not matter in very small teams or teams where there is across-the-board knowledge about accessibility, but for all other cases, I would highly recommend using the ARIA attributes in the CSS, too.

This was a great post, btw!

CollapseExpand
 
lkopacz profile image
Lindsey Kopacz
I'm a self-taught Front End & JS Dev and professional learner with accessibility expertise. I'm passionate about breaking down concepts into relatable concepts, making it more approachable.
  • Joined

True! I just never forget, but I definitely like the idea of forcing people to deal with it 😃

CollapseExpand
 
link2twenty profile image
Andrew Bone
A British web developer, that is passionate about web accessibility.
  • Location
    Britain, Europe
  • Pronouns
    He/Him
  • Work
    Senior Web Developer at bloc-digital
  • Joined

Great post, as always, Lindsey 🙂

CollapseExpand
 
teej profile image
TJ Fogarty
Software Engineer, mainly working in the Front End space.
  • Location
    Ireland
  • Education
    BSc Hons Multimedia Programming & Design
  • Work
    Technical Lead at Workhuman
  • Joined

Great post, thanks for sharing! I really like the code snippets that show what's changed.

CollapseExpand
 
lkopacz profile image
Lindsey Kopacz
I'm a self-taught Front End & JS Dev and professional learner with accessibility expertise. I'm passionate about breaking down concepts into relatable concepts, making it more approachable.
  • Joined

Really glad it helps you!

Going things step by step to highlight what changes fixes which issues really helps me learn why I am doing the thing 😁

CollapseExpand
 
kienngo profile image
ken
Front End Dev
  • Joined

Great material!

CollapseExpand
 
silvestricodes profile image
Jonathan Silvestri
Enjoys JavaScript, React, and video games.
  • Location
    New York City
  • Work
    Frontend Developer at InVision
  • Joined

Such a great post! I'm thinking of spinning up an example via a React component and seeing how it can be implemented there :)

CollapseExpand
 
itsjzt profile image
Saurabh Sharma
Fullstack Web developer.
  • Location
    Delhi, India
  • Work
    Fullstack Web Developer at Codeword Tech
  • Joined

really nice post, Is usingdetail element for accordation do any harm for accessibility or it's less used just because of low browser support

CollapseExpand
 
lkopacz profile image
Lindsey Kopacz
I'm a self-taught Front End & JS Dev and professional learner with accessibility expertise. I'm passionate about breaking down concepts into relatable concepts, making it more approachable.
  • Joined

Part of accessibility is being robust. In my opinion, this means support of browsers AND assistive devices.

Highly recommend giving this a read!developer.mozilla.org/en-US/docs/W...

CollapseExpand
 
anpos231 profile image
anpos231
  • Joined

If I remember correctly HTML has built in support for accordions natively (without JS!) But I don't remember how, or what tags were used for it. If anyone can find it, I'd be grateful.

CollapseExpand
 
lkopacz profile image
Lindsey Kopacz
I'm a self-taught Front End & JS Dev and professional learner with accessibility expertise. I'm passionate about breaking down concepts into relatable concepts, making it more approachable.
  • Joined

It's the<details> tag. It doesn't have full support which is why I went through this, but I included the reference in the "Adding the ARIA attributes" section!

CollapseExpand
 
mzahraei profile image
Ardalan
  • Education
    Software technology
  • Work
    - at -
  • Joined

Prefect

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I'm a self-taught Front End & JS Dev and professional learner with accessibility expertise. I'm passionate about breaking down concepts into relatable concepts, making it more approachable.
  • Joined

More fromLindsey Kopacz

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp