Счетчик при помощи замыкания и события event

Не могу понять как работает пример из интернета. На странице несколько div-ов с текстом (пусть будет 3). Хочу, чтобы при клике на каждый из них отображалось количество кликов по каждому. На каждый div вешать обработчик не собираюсь. В скрипте уже есть счетчик кликов. Не могу понять как нужно передать значение event.target в счетчик.

const widjet = document.getElementsByClassName('widjet')[0]
const wrapper = document.querySelector('.widjet__wrapper');
function test(elem) {
    let num = 1;
    return function() {
        num++;
        elem.textContent = String(num);
    }
}
wrapper.addEventListener('click', (event) => {
    let target = event.target;
     test(target);
})
     <div class="widjet__wrapper">
            <div class="widjet">0</div>
            <div class="widjet">0</div>
            <div class="widjet">0</div>
     </div>

  • Отдельно, счетчик на замыкании работает
  • Отдельно, работает event.target

У меня никак не получается их подружить.

Надо, чтобы работало вместе и чтобы при клике на каждый html-элемент, в его тексте был его счетчик.


Ответы (3 шт):

Автор решения: Leonid

Сохраняя общую логику автора кода, можно присвоить некому свойству test для каждого target при нажатии возвращаемую анонимную функцию из функции test, в замыкании которой мы имеем доступ как к num, так и к elem. Тут же вызываем эту функцию, привязанную к конкретному элементу. Далее по нажатии только вызываем ее.

const widjet = document.getElementsByClassName('widjet')[0]
const wrapper = document.querySelector('.widjet__wrapper');

function test(elem) {
  let num = 0;
  return function() {
    num++;
    elem.textContent = String(num);
  }
}
wrapper.addEventListener('click', (event) => {
  let target = event.target;
  if (!target.test) {
    target.test = test(target);
  }
  target.test();
})
<div class="widjet__wrapper">
  <div class="widjet">0</div>
  <div class="widjet">0</div>
  <div class="widjet">0</div>
</div>

→ Ссылка
Автор решения: SwaD

Самое разумное, это вешать на каждый div свой собственный обработчик событий:

const wrapper = document.querySelectorAll('.widjet');

function test(elem) {
  let num = 0;
  return () => elem.textContent = String(++num);
}

wrapper.forEach(el => {
  el.addEventListener('click', test(el));
})
<div class="widjet__wrapper">
  <div class="widjet">0</div>
  <div class="widjet">0</div>
  <div class="widjet">0</div>
</div>

Без этого, придется придумывать костыли, где и как хранить значения.

Если значения сохранять в свойстве html элемента или во внешней переменной, то это уже не замыкание получается.

→ Ссылка
Автор решения: ksa

На каждый div вешать обработчик не собираюсь.

Если так - значит нужно:

  • изменить счетчик
  • в обработчике работать с "target" события

Предложу вот такой вариант...

const wrapper = document.querySelector('.widjet__wrapper');

function test() {
  const num = new Map();
  return function(e) {
    const o = e.target.closest('.widjet')
    if (!o) return
    const v = num.has(o) ? num.get(o) + 1 : 1;
    num.set(o, v)
    o.textContent = v;
  }
}
wrapper.addEventListener('click', test())
<div class="widjet__wrapper">
  <div class="widjet">0</div>
  <div class="widjet">0</div>
  <div class="widjet">0</div>
</div>

→ Ссылка