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>
I added two things to the CSS:
- Margins around the buttons so they're not all right next to each other, so we can see them more easily
- 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;
}
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:
- You don't want the same code multiple times because if you have to change one you have to remember to change them all
- 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');
});
});
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');
})
}
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');
});
});
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)
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)
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.
yeah, i've not got that in there, I have:
i'm looking for whatever it is in my code that's stopping it from working.
got it, had to change my CSS
.btn:active,
.active
to
.btn button:active,
.active
EDIT: well I had it for a second.
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.
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.