DEV Community

Nic
Nic

Posted on

Multiple buttons looking like they're staying pressed - one at a time

In my last post I showed you how to make a button look like it stays pressed when you click on it. But what if you have multiple buttons which you want to have staying pressed when you click them - but only the last one you clicked. If you click one button, all the other buttons should look unpressed. The good news is, it's pretty similar to doing it for just one button.

Setting up the buttons

The HTML here has four buttons, the only difference from when we had one button, is that I numbered them.

<button class="button">Button 1</button>
<button class="button">Button 2</button>
<button class="button">Button 3</button>
<button class="button">Button 4</button>
Enter fullscreen mode Exit fullscreen mode

I added two things to the CSS:

  1. Margins around the buttons so they're not all right next to each other, so we can see them more easily
  2. Made buttons 2 & 4 a different colour so it looks pretty
.button {
  position: relative;
  margin: 0.5em;
  padding: 0.5em 1em;
  border: 0;
  border-radius: 0.5em;
  background-color: pink;
  box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.5);
    cursor: pointer;
}

.button:nth-child(even) {
  background-color: lightblue;
}

.button:active,
.active {
  top: 2px;
  left: 1px;
  box-shadow: none;
}
Enter fullscreen mode Exit fullscreen mode

Adding the JavaScript

This is a little different because we need to select all the buttons and listen out for each one being clicked. You could repeat what we had before four times, but:

  1. You don't want the same code multiple times because if you have to change one you have to remember to change them all
  2. If you need to add a fifth or a sixth button, it's a pain

So we loop through the buttons and listen to each one:

const buttons = document.querySelectorAll('.button');

buttons.forEach(button => {
  button.addEventListener('click', () => {
    button.classList.add('active');
    });
});
Enter fullscreen mode Exit fullscreen mode

Using querySelectorAll here allows us to use forEach. If you haven't seen it before, it's a quick way to loop through all of the buttons. A for loop will work equally well:

const buttons = document.querySelectorAll('.button');

for(let i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener('click', () => {
    buttons[i].classList.add('active');
  })
}
Enter fullscreen mode Exit fullscreen mode

However, if you try this code (whether with the forEach or for loop), you'll notice a problem. If you click Button 1 it stays pressed. If you then click Button 2 then both Button 1 and Button 2 are pressed. What we want is for Button 1 to become unpressed when Button 2 is clicked.

Unpressing buttons

What you could do, is to keep a record of which button was last clicked, and when a different one is clicked, remove the active class from it.

Or when a button is clicked, loop through all the other buttons checking if they have the active class and removing it if it is.

But those are a lot of work. What I do is when a button is clicked, loop through all the buttons and remove the active class, then add it to the button that was clicked. It feels a bit weird to remove the class and immediately add it again (even if it didn't need removing in the first place). But it is the least amount of work.

Technically, since not everything happens at exactly the same time, you might see all the buttons unpressed and then the one you clicked being pressed. But in reality this happens so fast that you're just not going to be able to see it.

In practise, it looks like this:

const buttons = document.querySelectorAll('.button');

buttons.forEach(button => {
  button.addEventListener('click', () => {
    buttons.forEach(button => button.classList.remove('active'));
    button.classList.add('active');
    });
});
Enter fullscreen mode Exit fullscreen mode

It's just one new line (or three if you use a for loop rather than forEach. And that's it, it works!

Here is the CodePen

Top comments (6)

Collapse
 
aagb1884 profile image
aagb1884

Hi, this is working for me up to a point - when I click on the button it changes when I'm clicking but it doesn't stay pressed once I release the button (also I am trying to keep multiple buttons pressed simultaneously so I'm actually keeping the problem code in tact)

Collapse
 
nicm42 profile image
Nic

It's really hard to know why without seeing your code. But if you want to keep multiple buttons looking pressed simultaneously, then you'd need to delete the part of the JS code that removes the active class.

Collapse
 
aagb1884 profile image
aagb1884 • Edited

yeah, i've not got that in there, I have:

const EurovisionCliche = ({cardArray}) => {

        const item1 = cardArray[0]
        const item2 = cardArray[1]
        const item3 = cardArray[2]
        const item4 = cardArray[3]
        const item5 = cardArray[4]
        const item6 = cardArray[5]

    const buttons = document.querySelectorAll('.btn');

    buttons.forEach(btn => {
      btn.addEventListener('click', () => {
        btn.classList.add('active');
        });
    });

    return ( 

        <div id = "container"> 

      <div className = "grid-container">

        <div className="grid-item">
          <button type = "button" id = "b1" class = "btn">{item1}</button>
        </div>
        <div className="grid-item">
          <button type = "button" id = "b2" class = "btn">{item2}</button>
        </div>
        <div className="grid-item">
          <button type = "button" id = "b3" class = "btn">{item3}</button>
        </div>
        <div className="grid-item">
          <button type = "button" id = "b4" class = "btn">{item4}</button>
        </div>
        <div className="grid-item">
          <button type = "button" id = "b5" class = "btn">{item5}</button>
        </div>
        <div className="grid-item">
          <button type = "button" id = "b6" class = "btn">{item6}</button>
        </div>

      </div>

    </div>

     );
}
Enter fullscreen mode Exit fullscreen mode

i'm looking for whatever it is in my code that's stopping it from working.

Thread Thread
 
aagb1884 profile image
aagb1884 • Edited

got it, had to change my CSS
.btn:active,
.active

to

.btn button:active,
.active

EDIT: well I had it for a second.

Thread Thread
 
nicm42 profile image
Nic

As long as your active css is making it look pressed down, then I don't see why it wouldn't work. I just put it in a JSFiddle and it works.

Thread Thread
 
nicm42 profile image
Nic

Both of those should work. If you inspect your buttons after you click them, do they have the active class? If they do, then there's something somewhere else in the CSS interfering with it. You should be able to find what it is in the dev tools.