Search code examples
javascriptfor-loopparametersevent-delegation

How to do Event delegation for an Array?(or NodeList)


I'm trying to use the Event delegation/switch statement for the first time in my life, and I'm having trouble with for loop. When it was 'array[i]' it wasn't a problem. But now I'm removing the for loop to use the event delegation and putting it inside of a function, it keeps giving me errors, and I don't know what parameter can replace (and make the code work again) that array[i] in the new function. Any help or explanation will be appreciated.

//original code

const numbers = document.querySelectorAll(".number");

for (let i = 0; i < numbers.length; i++) {
  numbers[i].addEventListener("click", function () {
    if (display.value.length < 13) {
      return;
    }
    if (display.value == "0" && numbers[i] != dot) {
      display.value = numbers[i].innerText;
      calculation = display.value;
    } else {
      if (numbers[i] == dot && display.value.includes(".")) {
        return;
      } else if (numbers[i] == dot && display.value == "") {
        return;
      } else {
        display.value += numbers[i].innerText;
        calculation = display.value;
      }
    }

    buttonEffect(numbers[i], "number-active");
  });
}
// New code

const numbers = document.querySelectorAll(".number");

function numberClick(number) {
  if (display.value.length > 13) {
    return;
  }
  if (display.value == "0" && this != dot) {
    display.value = number.innerText;
    calculation = display.value;
  } else {
    if (numbers == dot && display.value.includes(".")) {
      return;
    } else if (number == dot && display.value == "") {
      return;
    } else {
      display.value += number.innerText;
      calculation = display.value;
    }
  }
  operatorOnOff = false;
  buttonEffect(number, "number-active");
}




document.querySelector(".wrapper").addEventListener("click", (e) => {
  switch (e.target.dataset.key) {
    case "number":
      numberClick();
      break;
}
});

Solution

  • You pass the element that was the target of the click into numberClick and use it where previously you used numbers[i]. It looks like you're already doing the second part of that, and you even have a parameter declared for it, you just need to pass the element in:

    numberClick(e.target);
    

    Note that if your .number elements have child elements, target may be one of those child elements rather than .number. To handle that, you can use the DOM's relatively-new closest method, probably combined with contains to make sure it didn't match something surrounding .wrapper:

    document.querySelector(".wrapper").addEventListener("click", (e) => {
        const number = e.target.closest(".number");
        if (number && this.contains(number) && number.dataset.key) {
            numberClick(number);
        }
    });
    

    There are polyfills you can use if you need to support obsolete browsers, or just do the loop yourself:

    document.querySelector(".wrapper").addEventListener("click", (e) => {
        let number = e.target;
        while (number && !number.matches(".number")) {
            if (this === number) {
                return; // Reached the wrapper without finding it
            }
            number = number.parentElement;
        }
        if (number && number.dataset.key) {
            numberClick(number);
        }
    });