Search code examples
javascriptformsuser-interfacefocustabindex

How to dynamically select the next instance of a class after a click in native JavaScript?


In an effort to make my form more accessible, what I'm trying to figure out is that if the user presses enter on either the Agree or Disagree button, the tab-focus should skip to the next agree button. I've added a class to every instance of Agree buttons (next-btn), so now I just need to write a function that focuses on the next instance of next-btn.

The HTML is as follows:

<form id="quiz" style="text-align: center;">
<div id="questions">
    <div class="list-group-item">
        <p>I often make spontaneous or last-minute decisions</p>
        <div class="btn-group btn-group-justified button-wrap">
            <div class="btn-group"><input tabindex="-1" type="radio" name="q1" id="yes-q1" value="yellow" class="btn agree-btn" required/><label tabindex="0" class="button-label next-btn" for="yes-q1">Agree</label></div>
            <div class="btn-group"><input tabindex="-1" type="radio" name="q1" id="no-q1" value="blue" class="btn disagree-btn"/><label tabindex="0" class="button-label" for="no-q1">Disagree</label></div>
        </div>
    </div>
<div class="list-group-item">
    <p>My ideas are often unconventional or radical</p>
    <div class="btn-group btn-group-justified button-wrap"> 
        <div class="btn-group"><input tabindex="-1" type="radio" name="q2" id="yes-q2" value="yellow" class="btn agree-btn" required/><label tabindex="0" class="button-label next-btn" for="yes-q2">Agree</label></div>
        <div class="btn-group"><input tabindex="-1" type="radio" name="q2" id="no-q2" value="blue" class="btn disagree-btn" required/><label tabindex="0" class="button-label" for="no-q2">Disagree</label></div>
    </div>
</div>
            <div class="list-group-item">
                <p>I am naturally inclined to step forward and lead the group</p>
                <div class="btn-group btn-group-justified button-wrap">
                    <div class="btn-group"><input tabindex="-1" type="radio" name="q3" id="yes-q3" value="red" class="btn agree-btn" required/><label tabindex="0" class="button-label next-btn" for="yes-q3">Agree</label></div>
                    <div class="btn-group"><input tabindex="-1" type="radio" name="q3" id="no-q3" value="blue" class="btn disagree-btn" required/><label tabindex="0" class="button-label" for="no-q3">Disagree</label></div>
                </div>
            </div>

        </div>
        <button id="submit-btn" class="btn btn-primary btn-lg" type="submit" value="Submit" style="margin-top: 50px;">Submit</button>
    </form>

and the relevant JavaScript I have written is here:

const buttons = document.querySelectorAll(".button-label");

for(let answerButton of buttons) {
    answerButton.addEventListener('keydown', focusHere);

    // Simulate click event on tab + entering (accessiblity feature)
    function focusHere(event) {
        if (event.key == 'Enter') {
            // Trigger the click for the input
            answerButton.click();
            // Set focus to the next Agree button (next question)
            document.querySelector(".next-btn").focus();
        }
    }
}

Does anyone have suggestions on how I can specify the next instance of a class with my current form setup? I appreciate your help.


Solution

  • So I used Array#forEach to iterate through the array and get the index of the button.

    So I use the buttons array because it contains all the button.button-label elements, and the .next-btn elements have the .button-label class.

    So I slice the buttons array to return all the items in the array after the one that called the focusHere function, and found the first item or instance of the .next-btn element

    const buttons = [...document.querySelectorAll(".button-label")];
    
    buttons.forEach((answerButton, i) => {
        answerButton.addEventListener('keydown', focusHere);
    
        function focusHere(event) {
            if (event.key == 'Enter') {
                answerButton.click();
                buttons.slice(i).find(btn => btn.classList.contains('next-btn')).focus();
            }
        }
    });