ClassList и делегирование событий

Разбирать будем на примере такой вёрстки:

Код
<div id="first" class="btn-block">
  <button class="blue some"></button>
  <button></button>
  <button></button>
  <button></button>
  <button></button>
  <button></button>
  <button></button>
  <button></button>
</div>

Получим все кнопки по их селектору:

Код
const btns = document.querySelectorAll('button');

Мы можем узнать количество классов у первой кнопки:

Код
console.log(btns[0].classList.length); // 2

Получаем класс, находящийся у первой кнопки под индексом 0:

Код
console.log(btns[0].classList.item(0)); // blue

Добавим класс для первой кнопки, увидим, что количество классов у кнопки увеличилось:

Код
btns[0].classList.add('red');

console.log(btns[0].classList.length); // 3
console.log(btns[0].classList.item(2)); // red

Мы можем удалить какой-то класс у кнопки:

Код
btns[0].classList.remove('blue')

Очень удобная вещь, позволяющая отслеживать, есть ли у элемента заданный класс и добавлять либо удалять его.

Код
btns[0].classList.toggle('green');

Соответственно, если у кнопки не будет класса .green, он добавится, если же он есть, то уберётся.

В add() и remove() можно передавать сразу несколько классов:

Код
btns[0].classList.add('red', 'black');

C помощью метода contains() можно проверять наличие определённого класса на конкретном элементе (возвращается булиновое значение):

Код
console.log(btns[0].classList.contains('white')); // false

Где это можно использовать?

Например, при создании функционала гамбургер-меню, мы кликаем по кнопке и добавляем для меню и для самого гамбургера какой-то класс активности, при повторном клике мы проверяем, есть ли такой класс у меню и если он есть, класс удаляем, меню закрывается и гамбургер тоже.

Мы можем написать нечто подобное для наших кнопок: будем кликать по первой кнопке и проверять, если у второй кнопки есть класс .red, будем удалять его, если такого класса нет - будем его добавлять.
Практически, это тот же toggle, только прописанный через условия.

Код
btns[0].addEventListener('click', () => {
  if (!btns[1].classList.contains('red')) {
  btns[1].classList.add('red');
  } else {
  btns[1].classList.remove('red');
  }
});

В условии мы используем оператор отрицания(!), потому что, если у кнопки не окажется класса .red, в скобки вернётся false, а при false, как мы знаем, условие не выполнится, поэтому ставим знак отрицания, меняющий false на true, чтобы выполнился код в теле функции.

Устаревшее свойство className использовать не стоит, оно содержит в себе классы в качестве одной строки, это не очень удобно, так как приходится потом каким-то способом взаимодействовать с этой строкой, выбирая из неё нужное. 

Код
console.log(btns[0].className); // blue some

Делегирование событий


Допустим, у нас есть несколько кнопок и мы хотим, чтобы при клике на любую из них, вызывалось одно и тоже событие.

Можно повесить на каждую отдельную кнопку обработчик события, но если кнопки будут добавляться динамически, у них уже событий отслеживаться не будет.

А решить данную задачу можно с помощью делегирования событий.

Суть: берётся элемент (родительский) и на него вешается обработчик события:

Код
const wrapper = document.querySelector('.btn-block'); // родитель

wrapper.addEventListener('click', (event) => {

});

Event содержит всю информацию об элементе, на котором произошло событие.

Поэтому мы можем использовать для проверки, что мы кликнули именно в кнопку, а не в родительский элемент, условие:

Код
const wrapper = document.querySelector('.btn-block'); // родитель

wrapper.addEventListener('click', (event) => {
  if (event.target && event.target.tagName == 'BUTTON') {
  console.log('Hello!');
  }
});

В условии добавлено event.target для проверки, что элемент поддерживает событие click (рекомендация Google)

Свойство tagName - это встроенное свойство объекта event

Как вариант, можно проверять условие, что если у элемента есть определённый класс, например, .blue, тогда выполнять код:

Код
wrapper.addEventListener('click', (event) => {
  if (event.target && event.target.classList.contains('blue')) {
  console.log('Hello!');
  }
});

Теперь, если у какого-то элемента будет класс .blue, то на нём будет выполняться та функция, которую мы написали. Всё точно также будет срабатывать, если мы будем динамически добавлять новые элементы в родительский блок.

Код
wrapper.addEventListener('click', (event) => {
  if (event.target && event.target.tagName == "BUTTON") {
  console.log('Hello!');
  }
});

const btn = document.createElement('button'); // новый элемент
btn.classList.add('red'); // добавили ему класс
wrapper.append(btn); // поместили в родителя

И мы увидим, что у нас добавилась новая кнопка, и при клике по ней также как и при клике по всем остальным кнопкам, будет срабатывать наша функция.

Если же мы назначим обработчик события каждой кнопке с помощью перебора forEach():

Код
btns.forEach(btn => {
  btn.addEventListener('click', () => {
  console.log('Hello!');
  })
});

то мы увидим, что для динамически добавленной кнопки наша функция не сработает, поскольку она изначально не попала в перебор forEach()

У объекта event есть также метод, называемый matches(), который проводит проверку, что какой-то элемент совпадает с чем-то.

Мы можем это использовать, получим, что теперь только при клике именно на кнопку и именно с классом .red будет выполняться функция:

Код
wrapper.addEventListener('click', (event) => {
  if (event.target && event.target.matches('button.red')) {
  console.log('Hello!');
  }
});


index.htmlstyle.cssclassListmatchesДелегирование событий

Всего комментариев: 0

Имя *:
Email *:
Код *:
Хостинг от uCoz