1

I have multiple divs on the page with the class 'item' – I'd like to include a button within the div that when clicked will toggle append/remove the class 'zoom' on the 'item' div…

<div class="item">
<button class="zoomer"></button>
</div>

I've found plenty of code examples that target an id element, but struggling to find a solution that works with multiples of the same class element on one page.

Many thanks in advance!

2
  • Do you have buttons in all the divs with the class name item? If you do, you can add an event listener with queryselectorall then create a function to toggle classes on click. Commented Jun 30, 2022 at 21:49
  • 1
    I'll update my answer for your question. Commented Jun 30, 2022 at 21:55

3 Answers 3

3

You can use querySelectorAll to get all of the buttons and then you can use forEach so you can target the element's item parent.

// Get all the buttons
let zoomer_button = document.querySelectorAll('.zoomer');

// Loop through the buttons.
// Arrow function allows to pass the element
zoomer_button.forEach(button => {
  // Add an event listener for a click on the button.
  button.addEventListener('click', function(e) {
    // the e is the event, and then you check what the target is, which is the button.
    // then you can toggle a 'zoom' class on the parent 'item'
    e.target.parentNode.classList.toggle('zoom');
  });
});
.item.zoom {
  background-color: blue;
}
<div class="item">
  <button class="zoomer">button</button>
</div>
<div class="item">
  <button class="zoomer">button</button>
</div>
<div class="item">
  <button class="zoomer">button</button>
</div>
<div class="item">
  <button class="zoomer">button</button>
</div>
<div class="item">
  <button class="zoomer">button</button>
</div>

If it's nested a layer deeper, you can use parentNode twice.

// Get all the buttons
let zoomer_button = document.querySelectorAll('.zoomer');

// Loop through the buttons.
// Arrow function allows to pass the element
zoomer_button.forEach(button => {
  // Add an event listener for a click on the button.
  button.addEventListener('click', function(e) {
    // the e is the event, and then you check what the target is, which is the button.
    // then you can toggle a 'zoom' class on the parent 'item'
    e.target.parentNode.parentNode.classList.toggle('zoom');
  });
});
.item.zoom {
  background-color: blue;
}
<div class="item">
  <div class="media">
    <button class="zoomer">button</button>
  </div>
</div>
<div class="item">
  <div class="media">
    <button class="zoomer">button</button>
  </div>
</div>
<div class="item">
  <div class="media">
    <button class="zoomer">button</button>
  </div>
</div>
<div class="item">
  <div class="media">
    <button class="zoomer">button</button>
  </div>
</div>

Sign up to request clarification or add additional context in comments.

4 Comments

Updated with question. We could get into functions to do the nesting check, but this will work for basic needs.
Thank you! Works flawlessly. Throwing a curve ball… <div class="item"> <div class="media"> <button class="zoomer">button</button> </div> </div> What if the button is nested one layer deeper?
I think my edit beat your comment! Hahahaha!
You're way too quick!
1

You can use querySelectorAll and access each element with e.target

document.querySelectorAll('.item > .zoomer')
.forEach(elem => elem.addEventListener('click', e => {
  
  e.target.classList.toggle('someClass')
}))
.someClass{
  background:limegreen;
}
<div class="item">
<button class="zoomer">1</button>
</div>
<div class="item">
<button class="zoomer">2</button>
</div>
<div class="item">
<button class="zoomer">3</button>
</div>

<div class="item">
<button class="zoomer">4</button>
</div>

2 Comments

This doesn't answer the question, since it's not toggling a class on the parent, you are just adding content to the button.
i edited the post to toggle a CSS class
1

In the example below, are 7 <button>s that do various stuff -- details are commented in example.

// Render 7 <menu>/<button> combos 
[...new Array(7)].forEach((item, index) => {
  document.querySelector('main').insertAdjacentHTML('beforeend', `
<menu class="item${index}">
  <button class="btn${index}">${index}</button>
</menu>`);
});
/*~~~~~~~~~~~~~~~~~~~~~~~.btn0*/

// Click <button> remove it's parent (which also removes the <button>)
document.querySelector('.btn0').onclick = function(e) {
  this.parentElement.remove();
}
/*~~~~~~~~~~~~~~~~~~~~~~~.btn1*/

// Click <button> -- <button> is removed but it's contents is left behind
document.querySelector('.btn1').onclick = unWrap;

function unWrap(e) {
  const clicked = e.target;
  const parent = clicked.parentElement;
  while (clicked.firstChild) {
    parent.insertBefore(clicked.firstChild, clicked);
  }
  clicked.remove();
}
/*~~~~~~~~~~~~~~~~~~~~~.btn4-6*/

// Collect all tags with a class that starts with "btn"
const btns = document.querySelectorAll("[class^='btn']");

// Adding .target class to the last 2 <button>s
btns.forEach((btn, idx) => {
  if (idx > 4) btn.classList.add('target')
});
/*~~~~~~~~~~~~~~~~~~~~~~~.btn2*/

// Target third <button> by index
/*
When <button> clicked, it's parent gets .hide class which is:
visibility:hidden which would normally hide the <button> as well, but
.btn2 has visibility explicitly set to visible
*/
btns[2].onclick = e => e.target.closest('menu').classList.toggle('hide');
/*~~~~~~~~~~~~~~~~~~~~~~~.btn3*/

/*
Everytime the <button> is clicked, a copy of itself is made and the
clones also have this ability as well
*/
btns[3].addEventListener('click', copySelf);

function copySelf(e) {
  let dupe = e.target.cloneNode(true);
  e.target.parentElement.append(dupe);
  dupe.onclick = copySelf;
}
/*~~~~~~~~~~~~~~~~~~~~~.btn4-6*/

/*
The click event is bound to the parent/ancestor tag <section>
Any click to any <button> will trigger the event handler. 
.btn4, .btn5, and .btn6 all react in a specific manner because
the event handler, delegateClick(e) is using flow control statements and 
specific criteria.
*/
document.querySelector('main').onclick = delegateClick;

let armed = false;

function delegateClick(e) {
  const clicked = e.target;
  if (clicked.matches('button') && !armed) {
    clicked.classList.add('armed');
    armed = true;
    return;
  }

  if (clicked.matches('.armed.target') && armed) {
    clicked.parentElement.style.cssText = `font-size: 5rem; margin: 0`
    clicked.replaceWith(`💥`);
    return;
  }

  if (clicked.matches('.target') && armed) {
    clicked.classList.add('armed');
    return;
  }

  if (clicked.matches('.armed') && armed) {
    clicked.classList.remove('armed');
    armed = false;
  }
}
menu {
  outline: dashed red 1px;
}

.hide {
  visibility: hidden;
}

.btn2 {
  visibility: visible
}

.armed {
  animation: warning 1s linear infinite;
}

@keyframes warning {
  50% {
    opacity: 0;
  }
}

.target.armed {
  background: red;
  color: white;
}

button {
  font: inherit;
  cursor: pointer;
}
<main></main>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.