skip to Main Content

I have a set of 4 items and one button. Clicking on an item should add that item to an empty array. Then clicking the button will display the number of items selected.

I’ve looped through the HTML Collection and used the push method to add each item as they are clicked. However, each additional click adds a duplicate of the original item to the array. So instead of the array length totaling 4, the total ends up being 10.

Here’s an example from the console per each click:

["item 2"]
["item 2", "item 2", "item 3"]
["item 2", "item 2", "item 3", "item 2", "item 3", "item 4"]
["item 2", "item 2", "item 3", "item 2", "item 3", "item 4", "item 1", "item 2", "item 3", "item 4"]

I think the issue is related to event bubbling, but I am not sure how to proceed.

Here’s the FIDDLE.

const noticeMod = document.getElementById('notice');
const noticeInner = document.getElementById('message');
const windowOverlay = document.getElementById('overlay');
let itemsSelected = [];

function appendMessage() {
  let elSpan = document.createElement('span');
  elSpan.textContent = `You Downloaded ${itemsSelected.length} items.`;
  noticeInner.appendChild(elSpan);
}

function removeMessage() {
  let emptySpan = document.querySelectorAll('#message span');
  emptySpan[0].remove();
}

function openNotification() {
  noticeMod.classList.add('show-notice');
  windowOverlay.classList.add('show-overlay');
}

function closeNotification() {
  noticeMod.classList.remove('show-notice');
  windowOverlay.classList.remove('show-overlay');
}

function addItems() {
  const currentItem = document.getElementsByClassName('item');

  for (i = 0; i < currentItem.length; i++) {
    let itemName = document.querySelectorAll('.item-title')[i].textContent;
    if (currentItem[i].classList.contains('item-selected')) {
      itemsSelected.push(itemName);
    }

  }

  console.log(itemsSelected);

}

//addItems();

document.addEventListener('click', function(event) {

  if (event.target.matches('#submitBtn')) {
    appendMessage();
    openNotification();
  }

  if (event.target.matches('#closeBtn') || event.target.matches('#overlay')) {
    removeMessage();
    closeNotification();
  }

  if (event.target.matches('.item-selected')) {
    event.target.classList.remove('item-selected');
  } else if (event.target.matches('.item')) {
    event.target.classList.add('item-selected');
    addItems();
  }

}, false);
body {
  background: #191919;
  color: #fff;
  position: relative;
}

.notice-modal {
  background: #242424;
  border: 1px solid #555;
  position: fixed;
  right: 0;
  top: 12px;
  transform: translateX(100%);
  transition: 500ms ease;
  z-index: 1000;
}

.show-notice {
  right: 12px;
  transform: translateX(0%);
  transition: 500ms ease;
}

.close span {
  cursor: pointer;
  border-left: 2px solid #555;
  padding-left: 12px;
  margin-left: 12px;
}

.transparent-overlay {
  position: fixed;
  display: none;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  cursor: pointer;
  background-color: #191919;
  opacity: 0;
  z-index: 990;
}

.show-overlay {
  display: block;
}

.row {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  flex-wrap: wrap;
  padding: 12px;
}

.item {
  cursor: pointer;
  background: #242424;
  padding: 20px;
  border: 3px solid #242424;
  margin-bottom: 12px;
}

.item-selected {
  border: 3px solid #2269a8;
}

.item-title {
  font-size: 18px;
}

.cta {
  margin: 0 auto;
  padding: 12px;
}

#submitBtn {
  cursor: pointer;
  border: none;
  background: #2269a8;
  padding: 12px;
  color: #fff;
  width: 100%;
}

#submitBtn:hover {
  background: #235596;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js"></script>
<aside id="notice" class="notice-modal">
  <div class="row">
    <div id="message"></div>
    <div class="close">
      <span id="closeBtn">&#10005;</span>
    </div>
  </div>

</aside>

<div id="overlay" class="transparent-overlay"></div>

<section class="row">

  <div class="item">
    <h1 class="item-title">item 1</h1>
  </div>
  <div class="item">
    <h1 class="item-title">item 2</h1>
  </div>
  <div class="item">
    <h1 class="item-title">item 3</h1>
  </div>
  <div class="item">
    <h1 class="item-title">item 4</h1>
  </div>

</section>

<section class="cta">
  <button id="submitBtn">Submit</button>
</section>

2

Answers


  1. The problem is with your addItems method. How do you identify the currently clicked item? Every time you call the addItems method it finds all the items with class name item-selected. So at first click there is only one item selected. But after each click you will get all the previously clicked item. Now there is two way,

    If you are sure that the item names are unique then in the addItems method you can check for duplicate items and add only the new ones.

    function addItems() {
        const currentItem = document.getElementsByClassName('item');
    
        for(i = 0; i < currentItem.length; i++) {
            let itemName = document.querySelectorAll('.item-title')[i].textContent;
            if (currentItem[i].classList.contains('item-selected') && itemsSelected.indexOf(itemName) === -1) {
                 itemsSelected.push(itemName);
            }  
    
         }
    
         console.log(itemsSelected);
    
    }
    
    1. If you are not sure if the names are unique, then you can pass the currently clicked element to the addItems method.

      function addItems(currentElement) {
          let itemName = currentElement.innerText;
          if (itemsSelected.indexOf(itemName) === -1) {
                itemsSelected.push(itemName);
          }  
          console.log(itemsSelected);
      }
      

    and call the addItems method like this,

    addItems(event.target);
    
    Login or Signup to reply.
  2. I hope this example will help you.
    In the example, values ​​are added on the first click and removed on the second click.

    let array = []
    
    document.querySelector('.container').addEventListener('click', (e) => {
      if (e.target.classList.contains('item')) {
        e.target.classList.toggle('active')
        const index = array.indexOf(e.target.textContent)
        if (index !== -1) {
          array = array.filter((e, i) => i !== index)
        } else {
          array.push(e.target.textContent)
        }
        console.log(array)
      }
    })
    .item {
      background-color: lightgreen;
      width: 100px;
      height: 100px;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      border: 2px dotted green;
    }
    
    .item:hover {
      cursor: pointer;
    }
    
    .item.active {
      border-style: solid;
    }
    
    .container {
      width: 70%;
      margin: 0 auto;
      display: flex;
      justify-content: space-between;
    }
    <div class="container">
      <div class="item">item 1</div>
      <div class="item">item 2</div>
      <div class="item">item 3</div>
      <div class="item">item 4</div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search