Home > Software design >  Loop through elements by class name, click any element within the array, but only affect the element
Loop through elements by class name, click any element within the array, but only affect the element

Time:09-28

I am working on a WordPress site and I have a snippet of html that iterates with repeating classes.

I am attempting to create a click function but only affect the element that is clicked. All in JavaScript.

As of right now my function is affecting all elements with the class name. Test code can be found at my CodePen or below.

I can accomplish this without nested loops as seen here. So my assumption is the problem lies within the second forEach loop. I would appreciate any light on the matter. Thank you in advance.

/**
 *Constructors
 **/
const carousel = document.getElementsByClassName("carousel");
const btns = document.getElementsByClassName("btns");

/**
 *Execute
 **/

Array.from(btns).forEach((i) => {
  i.addEventListener("click", (e) => {
    Array.from(carousel).forEach((n) => {
      if (i.classList.contains("slide-left")) {
        n.scrollLeft -= 20;
      } else if (i.classList.contains("slide-right")) {
        n.scrollLeft  = 20;
      } else {
        alert("ut oh");
      }
    });
  });
});
/*
**Utilities
*/


/*containers*/

.feed-container {
  position: absolute;
  height: 200px;
  width: 100%;
  display: grid;
  grid-template-columns: 1;
  grid-template-rows: 1;
}

.carousel {
  grid-row: 1;
  grid-column: 1/5;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: 1;
  grid-gap: 15px;
  align-self: center;
  border: 1px solid #ccc;
  overflow-x: scroll;
  overflow-y: hidden;
}


/*div-buttons*/

div[class*="slide-"] {
  /*opacity: 0;*/
  position: sticky;
  grid-row: 1;
  z-index: 5;
  place-self: center;
  transition: 0.5s;
  padding: 15px;
}

.slide-left {
  grid-column: 1;
}

.slide-right {
  grid-column: 4;
}


/*items*/

div[class*="item-"] {
  grid-row: 1;
  width: 400px;
  height: 200px;
}

.item-1 {
  background: blue;
}

.item-2 {
  background: red;
}

.item-3 {
  background: grey;
}

.item-4 {
  background: yellow;
}


/*scrollbar*/

::-webkit-scrollbar {
  display: none;
}


/*chevrons*/

[class*="chevron-"] {
  box-sizing: border-box;
  position: relative;
  display: block;
  transform: scale(var(--ggs, 1));
  width: 22px;
  height: 22px;
  border: 2px solid transparent;
  border-radius: 25px;
}

[class*="chevron-"]::after {
  content: "";
  display: block;
  box-sizing: border-box;
  position: absolute;
  width: 40px;
  height: 40px;
  border-bottom: 8px solid;
  border-left: 8px solid;
  bottom: 0;
}

.chevron-left::after {
  transform: rotate(45deg);
  left: 15px;
}

.chevron-right::after {
  transform: rotate(-135deg);
  right: 15px;
}


/*
**Exceptions
*/

.btns:hover {
  cursor: pointer;
}

.opaque {
  opacity: 1 !important;
}

.show {
  display: block;
}
<div id="wrapper" style="display:grid; grid-template-rows:repeat(2, auto); grid-gap: 100px;">
  <div>
    <h1>Header</h1>
    <div class="feed-container">
      <div class="carousel">
        <div class="item-1"></div>
        <div class="item-2"></div>
        <div class="item-3"></div>
        <div class="item-4"></div>
      </div>
      <div class="slide-left btns">
        <div class="chevron-left"></div>
      </div>
      <div class="slide-right btns">
        <div class="chevron-right"></div>
      </div>
    </div>
  </div>
  <br>
  <div>
    <h1>Header</h1>
    <div class="feed-container">
      <div class="carousel">
        <div class="item-1"></div>
        <div class="item-2"></div>
        <div class="item-3"></div>
        <div class="item-4"></div>
      </div>
      <div class="slide-left btns">
        <div class="chevron-left"></div>
      </div>
      <div class="slide-right btns">
        <div class="chevron-right"></div>
      </div>
    </div>
  </div>
</div>

CodePudding user response:

It's because you're getting all the elements with class name carousel and then looping through them with each click.

   const carousel = document.getElementsByClassName("carousel");

Instead what you need to do is get the carousels only under the button's parent when you trigger the click event

eg something like this:

Array.from(btns).forEach((i) => {
    i.addEventListener("click", (e) => {
        const targetElement = e?.target || e?.srcElement;
        const parent = targetElement.parentElement();
        const carousel = Array.from(parent.getElementsByClassName("carousel"));
        
        carousel.forEach((n) => {
            if (i.classList.contains("slide-left")) {
                n.scrollLeft -= 20;
            } else if (i.classList.contains("slide-right")) {
                n.scrollLeft  = 20;
            } else {
                alert("ut oh");
            }
        });
    });
});

CodePudding user response:

I took a look at the following as recommend and it seems to do the trick.

  • I created a variable that calls the parentNode "forEach()" button clicked. Oppose to looping through each element.

Working example, codePen

const carousel = document.querySelectorAll(".carousel");
const btns = document.querySelectorAll(".btns");

 btns.forEach((i) => {
  i.addEventListener("click", () => {
    var x = i.parentNode;
    var y = Array.from(x.querySelectorAll(".carousel"));
    y.forEach((n) => {
      if (i.classList.contains("slide-left")) {
        n.scrollLeft -= 20;
      } else {
        n.scrollLeft  = 20;
      }
    });
  });
});

  • Related