Photo by The Lucky Neko on Unsplash
How to Make an Accordion FAQ with JS - part 2
Part 2: let's get accessible
This is part two of my three-part accordion FAQ tutorial. Part two is on accessibility, part three is on making the project look pretty. Why? Because I believe accessibility (a11y) is more important, making sure everyone can use something is more important than how it looks.
And that isn't to say I'm an expert on a11y, I'm not. I am, however, constantly working to improve and continue to make it a priority in my code.
At the end of part one, we had a very bare project:
HTML
<article class="q">
<div class="title">
<h2>Question One</h2>
<button>Clicky</button>
</div>
<div class="answer">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
</article>
SCSS
.answer {
display: none;
}
.clicked {
.answer {
display: block;
}
}
JavaScript
const question=document.querySelectorAll('.q');
question.forEach(function (quest) {
const btn=quest.querySelector('button');
btn.addEventListener('click', function() {
quest.classList.toggle('clicked');
});
});
We won't be adding much code for this step. Why? By using HTML5 elements, we've already achieved greater a11y. We structured our project using elements that are meaningful to assistive technology. Each question is an article
, and in my code, (where this is a stand-alone project) the questions are wrapped in <main>
. If this is part of a page with more information, wrapping them in <section>
nested inside <main>
would be best.
These elements are helpful for assistive technology, as opposed to using just <div>
elements that don't automatically provide structure.
Adding an ARIA Attribute
We need to add code so that assistive technology can tell the user if an answer is showing or not. We do that by adding the aria-expanded
attribute to our <article>
, like so:
<article class="q" aria-expanded="false">
This will let users know there's more information that isn't currently visible. Doing this alone is not enough, we have to toggle the attribute to "true"
when the answer is showing. To do that, we go back to the JavaScriptmobile!
JavaScriptmobile
Inside our eventListener
we need to declare a variable for the aria-expanded
attribute of the specific question (quest
) that is clicked, and we need to use getAttribute
to do so.
let ariaValue=quest.getAttribute("aria-expanded");
If we'd print ariaValue
to the console right now, it would display "false", no matter how many times we click the button. To remedy that, we'll use a ternary operator. A ternary operator looks at a condition and executes an expression if it's true, a different expression if it's false. In our case, it will look to see if the value
of the aria-expanded attribute
is true or false, and then set it to the opposite. Keep this in mind as this next part may be confusing at first.
ariaValue = (ariaValue === 'false')
? quest.setAttribute('aria-expanded', 'true')
: quest.setAttribute('aria-expanded', 'false');
Now let's break this down a little: ariaValue =
is just us wanting to store something in that variable. ariaValue === "false" ?
is the condition that's being evaluated, if it evaluates true (meaning the current value is "false"), then aria-expanded
is set as true
.
If the condition evaluates false (meaning the current value is "true"), then our attribute
is set to false
. If you're thinking I'm trying to tell you that if something is false, and it should be true it gets set to true, and if something is supposed to be false, and it is false that it's false, it gets set to false anyway... You're correct!
Here's a less maddening example:
var skyColor = 'blue';
var timeOfDay = (skyColor === 'blue') ? 'day' : 'night';
console.log(timeofDay); //'day'
Currently, skyColor
is blue, because that's what the variable is initialized as. The second variable, timeOfDay
is going to evaluate the condition (skyColor === 'blue')
, and if that expression is found to be true, timeOfDay
stores day
.
var skyColor = 'black';
var timeOfDay = (skyColor === 'blue') ? 'day' : 'night';
console.log(timeofDay); //'night'
In this example, the expression is false, so timeOfDay
stores 'night'.
If your brain isn't a tangled mess after all that, congratulations! You're done! If it is a tangled mess, congratulations! You're still done! And here's a bandage for your brain.
Visual Effects
Here's a gif of a button being clicked and the aria-expanded
value changing (our project doesn't look like this yet):
I really hope you enjoyed this article and found it helpful. Leave a comment below if you have any questions. Tune in next week for when we style this project!