Search code examples
javascriptarraysloops

Using a looped element as a new child in replaceChild() method


const addToCartButtons = document.getElementsByClassName('add-to-cart');
const addToCartButtonsArray = Array.from(addToCartButtons);

const increment = document.createElement('img');
increment.setAttribute('src', '/assets/images/icon-increment-quantity.svg');
increment.style.zIndex = '0';
increment.style.width = '15px';
increment.style.marginBottom = '0.5px';
increment.style.marginRight = '6px';
increment.style.border = 'none';
increment.style.cursor = 'pointer';

const decrement = document.createElement('img');
decrement.setAttribute('src', '/assets/images/icon-decrement-quantity.svg');
decrement.style.zIndex = '1';
decrement.style.width = '15px';
decrement.style.marginBottom = '0.5px';
decrement.style.marginLeft = '6px';
decrement.style.border = 'none';
decrement.style.cursor = 'pointer';

const quantity = document.createElement('span');
quantity.textContent = 0;

const addToCartButtonOnHover = document.createElement('div'); // new element when hovering
addToCartButtonOnHover.classList.add('add-to-cart-on-hover');

addToCartButtonsArray.forEach((button, index) => {

  button.addEventListener('mouseover', (e) => {

    const element = e.target.parentElement;

    element.replaceChild(addToCartButtonOnHover, element.children[0]); // replaces old add-to-cart button with the hover one

    addToCartButtonOnHover.append(decrement, quantity, increment);

    let count = 0;

    increment.onclick = function() {
      count += 1;
      quantity.textContent = count;
    }
    // adding and deducting products
    decrement.onclick = function() {
      count -= 1;
      if (count < 0) {
        count = 0;
      }
      quantity.textContent = count;
    }
  });
   addToCartButtonOnHover.addEventListener('mouseleave', (e) => {
      const element = e.target.parentElement;
      element.replaceChild(addToCartButtonsArray[index], element.children[0]); // returns the old add-to-cart button
});
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: 'Red Hat Text';
}

body {
  display: flex;
  height: 100vh;
  justify-content: space-evenly;
}

.products {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-evenly;
  align-items: center;
}

.product {
  flex-direction: column;
  align-items: center;
  width: 30%;
  height: 43vh;
  margin-left: 0;
  margin-bottom: 5%;
  margin: 50px;
}

.add-to-cart {
  border: 1px solid hsl(14, 65%, 9%);
  border-radius: 20px;
  margin-left: 16%;
  text-align: center;
  width: 10vw;
  height: 5vh;
  line-height: 4.5vh;
  background-color: white;
}

.add-to-cart-on-hover {
  border: 1px solid hsl(14, 65%, 9%);
  border-radius: 20px;
  text-align: center;
  margin-left: 16%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 10vw;
  height: 5vh;
  background-color: orange;
}
  <div class="products">
    <div class="product">
      <div class="add-to-cart">Add to cart</div>
    </div>
    <div class="product">
      <div class="add-to-cart">Add to cart</div>
    </div>
  </div>

Is it possible to somehow use an iterated element as a new child (the first argument) in the replaceChild() function? My code currently works as intended only on the first button because in the second event listener it's accessed like 'array[0]'. What I want is that the second button acts same as the first, but if I put something like 'array[1]' then the second button works but the first one disappears. I tried looping through the addToCartButtonsArray inside the event listener but it doesn't work. I want both buttons to work the same way.


Solution

  • forEach has the index as the second parameter of the callback, so you can define it and use it. Example:

    let array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
    
    array.forEach((item, index) => {
        console.log(`${index}: ${item}`);
    });

    So you will have nothing else to do than specify your function as

    addToCartButtonsArray.forEach((button, index) => {
        //your code here
    });
    

    and then use [index] instead of [0]

    const addToCartButtons = document.getElementsByClassName('add-to-cart');
    const addToCartButtonsArray = Array.from(addToCartButtons);
    
    const increment = document.createElement('img');
    increment.setAttribute('src', '/assets/images/icon-increment-quantity.svg');
    increment.style.zIndex = '0';
    increment.style.width = '15px';
    increment.style.marginBottom = '0.5px';
    increment.style.marginRight = '6px';
    increment.style.border = 'none';
    increment.style.cursor = 'pointer';
    
    const decrement = document.createElement('img');
    decrement.setAttribute('src', '/assets/images/icon-decrement-quantity.svg');
    decrement.style.zIndex = '1';
    decrement.style.width = '15px';
    decrement.style.marginBottom = '0.5px';
    decrement.style.marginLeft = '6px';
    decrement.style.border = 'none';
    decrement.style.cursor = 'pointer';
    
    const quantity = document.createElement('span');
    quantity.textContent = 0;
    
    
    addToCartButtonsArray.forEach((button, index) => {
    
    const addToCartButtonOnHover = document.createElement('div'); // new element when hovering
    addToCartButtonOnHover.classList.add('add-to-cart-on-hover');
      button.addEventListener('mouseover', (e) => {
    
        const element = e.target.parentElement;
    
        element.replaceChild(addToCartButtonOnHover, element.children[0]); // replaces old add-to-cart button with the hover one
    
        addToCartButtonOnHover.append(decrement, quantity, increment);
    
        let count = 0;
    
        increment.onclick = function() {
          count += 1;
          quantity.textContent = count;
        }
        // adding and deducting products
        decrement.onclick = function() {
          count -= 1;
          if (count < 0) {
            count = 0;
          }
          quantity.textContent = count;
        }
      });
       addToCartButtonOnHover.addEventListener('mouseleave', (e) => {
          const element = e.target.parentElement;
          element.replaceChild(addToCartButtonsArray[index], element.children[0]); // returns the old add-to-cart button
    });
    });
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: 'Red Hat Text';
    }
    
    body {
      display: flex;
      height: 100vh;
      justify-content: space-evenly;
    }
    
    .products {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-evenly;
      align-items: center;
    }
    
    .product {
      flex-direction: column;
      align-items: center;
      width: 30%;
      height: 43vh;
      margin-left: 0;
      margin-bottom: 5%;
      margin: 50px;
    }
    
    .add-to-cart {
      border: 1px solid hsl(14, 65%, 9%);
      border-radius: 20px;
      margin-left: 16%;
      text-align: center;
      width: 10vw;
      height: 5vh;
      line-height: 4.5vh;
      background-color: white;
    }
    
    .add-to-cart-on-hover {
      border: 1px solid hsl(14, 65%, 9%);
      border-radius: 20px;
      text-align: center;
      margin-left: 16%;
      display: flex;
      justify-content: space-between;
      align-items: center;
      width: 10vw;
      height: 5vh;
      background-color: orange;
    }
    <div class="products">
        <div class="product">
          <div class="add-to-cart">Add to cart</div>
        </div>
        <div class="product">
          <div class="add-to-cart">Add to cart</div>
        </div>
      </div>