Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Почему нельзя обратиться к элементу по его классу в шаблоне? (https://javascript.ru/forum/events/81406-pochemu-nelzya-obratitsya-k-ehlementu-po-ego-klassu-v-shablone.html)

Vladikslav 21.11.2020 02:25

Почему нельзя обратиться к элементу по его классу в шаблоне?
 
Коллеги, привет.
Почему не работает событие на элементе, созданном в рамках шаблона при помощи другого события?

Вот забацал латй-код для иллюстрации проблемы

Смысл такой. Написал функцию, которая из массива объектов собирает данные, подставляет их в шаблон и аппендит его в секцию DOM-а.
В массиве некоторое количество объектов с данными.
Задача вывести первый шаблон, полученный из первого объекта массива сразу, т.е. при событии DOMContentLoaded я просто запускаю данную функцию. А далее при клике на ссылку "Ещё..." выводить шаблон, полученный из следующего объекта данного массива объектов, ну и так далее выводить по одному следующие шаблоны.

В шаблоне присутствует элемент <a>, на который подвешено событие click, в котором, например, прописано alert('Yo!');
Так вот загвоздка в том, что это событие срабатывает только на первом созданном шаблоне, который генерился при DOMContentLoaded.
На последующих шаблонах событие click у данного элемента не срабатывает:
const testLinks = document.getElementsByClassName("test-link");
for(let testLink of testLinks) {
	testLink.addEventListener('click', function(event) {
		let t = event.target;
		console.log(t);
		alert ('Yo!');
	});
}

Но интересно то, что инспектор видит в доме созданный шаблон, однако показывает, что в самом первом шаблоне на элементе <a> event висит, а на остальных, сгенерированных по событию click ("Ещё..") данного ивента на их элементе <a> нет! (см. пример).

И ещё. Проверка на target при делегировании события родителю работает только в том случае, если родитель лежит за пределами генерируемого html-шаблона. Т.е. в случае с body и проверкой на target наличие нужного класса .test-link событие срабатывает.

ПОЧЕМУ ТАК ПРОИСХОДИТ?!! ПОМОГИТЕ! В ЧЁМ ЗАГВОЗДКА?

ЗЫ: делегирование не подходит мне, так как я на реальном проекте использую сторонние библиотеки, которые ни хрена не отрабатывают события при таком раскладе.

Заранее спасибо.

Nexus 21.11.2020 03:19

Vladikslav, Честно говоря не дочитал ваше сообщение, т.к. ответ на вопрос стал ясен после ознакомления с макетом и вашим кодом.

Цитата:

Сообщение от Vladikslav
Почему не работает событие на элементе, созданном в рамках шаблона при помощи другого события?

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

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

Гуглите «js event delegation».

Nexus 21.11.2020 03:25

Набросал на коленке примерное решение:
document.addEventListener('DOMContentLoaded', () => {
    const wrapper = document.querySelector('.objects');
    if (!wrapper) return;
    
    const selector = '.test-link';
    wrapper.addEventListener('click', e => {
        let target = e.target;
        if (!target || !target.matches(selector) && !(target = target.closest(selector))) {
            return;
        }
        
        console.log(target);
        
        alert('Yo!');
    });
});

voraa 21.11.2020 08:04

Цитата:

Сообщение от Nexus
 if (!target || !target.matches(selector) && !(target = target.closest(selector))) {

Достаточно
if (!target || !(target = target.closest(selector))) {

(А когда может быть !target ?)

Nexus 21.11.2020 14:21

Цитата:

Сообщение от voraa
А когда может быть !target ?

Не буду утверждать, но вроде когда-то ловил ошибку из-за отсутствия target, поэтому здесь проверка просто «на всякий случай» :)

p.s. спасибо за подсказку с .closest, почему-то не думал, что метод возвращает «ближайший родительский элемент (или сам элемент)».

Если предположить, что у события всегда будет свойство target, то условие можно сильно сократить:
if (!target.closest(selector)) return;

Vladikslav 22.11.2020 01:12

Цитата:

Сообщение от Nexus (Сообщение 531003)
Vladikslav, Честно говоря не дочитал ваше сообщение, т.к. ответ на вопрос стал ясен после ознакомления с макетом и вашим кодом.
Потому что вы вешаете слушатель события на существующие в данный момент элементы.
Подгружаемые элементы появятся только после нажатия пользователем на кнопку, ваш код к этому моменту уже отработает.

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

Гуглите «js event delegation».


Спасибо за разъяснение, зря не дочитали до конца.

Цитата:

ЗЫ: делегирование не подходит мне, так как я на реальном проекте использую сторонние библиотеки, которые ни хрена не отрабатывают события при таком раскладе.
Я так собственно и думал как вы и описали, с делегированием предварительно тестировал и это действительно работает. Меня сбило с толку другое. Просто я для галереи изображений использовал Fullscreen Lightbox, а там JS аглифицирован и в него не залезть. Но RTFM как обычно рулит :) Залез в их доку а там о чудо:

Цитата:

After adding new <a> tags to detect new lightboxes or update existing ones use global refreshFsLightbox() function.
const a = document.createElement('a');
a.setAttribute('data-fslightbox', 'gallery');
a.setAttribute('href', 'images/2.jpg');
document.body.appendChild(a);

refreshFsLightbox();


Магия!!! Ребята позаботились об этом! :dance:
https://fslightbox.com/javascript/do...use#refreshing

Короче, разобрался с этим сам, можете мне карму повысить, а то я что-то в нулях засиделся здесь почему-то :victory:


Часовой пояс GMT +3, время: 03:51.