Войти Регистрация
Фронтенд

39. Делегирование кликов в списке объявлений Авито без DOM

Сложная

В выдаче Авито 50 карточек, в каждой кнопки «В избранное» и «Позвонить». Вешать 100 обработчиков и перевешивать их при подгрузке страницы дорого, поэтому слушают один раз — на списке — и по цели клика решают, что нажато.

Соберите модель этого механизма без браузера. Класс EventTree(tree) получает дерево { id, tag, cls, children }, где cls — классы через пробел. on(nodeId, type, selector, handler) вешает обработчик на узел. emit(targetId, type) запускает событие на узле-цели.

Событие всплывает: от цели к корню, на каждом узле обработчики этого типа вызываются в порядке подписки. Если selector равен null, обработчик срабатывает всегда. Иначе ищем ближайший к цели узел на пути от цели до узла с обработчиком — сам этот узел не считается, — подходящий под селектор. Нашли — вызываем с event.delegateTarget = id найденного, нет — пропускаем.

Селектор — tag, #id, .класс, их сочетание вида button.fav.active. Класс совпадает целиком: .fa не подходит к fav. Объект события: type, target, currentTarget, delegateTarget (или null), stopPropagation() — дорабатывают обработчики текущего узла, выше не идём; stopImmediatePropagation() — останавливаемся сразу.

В работе: Списки и таблицы с подгружаемыми строками, обработчики на document, свои UI-библиотеки.

Пример 1: клик по иконке внутри кнопки

Аргументы теста
{"id": "page", "tag": "main", "cls": "", "children": [{"id": "list", "tag": "ul", "cls": "items", "children": [{"id": "ad1", "tag": "li", "cls": "card", "children": [{"id": "photo1", "tag": "img", "cls": "photo"}, {"id": "fav1", "tag": "button", "cls": "fav", "children": [{"id": "icon1", "tag": "svg", "cls": "icon"}]}, {"id": "call1", "tag": "button", "cls": "call promo"}]}, {"id": "ad2", "tag": "li", "cls": "card vip", "children": [{"id": "fav2", "tag": "button", "cls": "fav active"}]}]}]}, [["on", "list", "click", "button.fav", "F", null], ["emit", "icon1", "click"]]
Ожидается
["F:list>fav1"]

Пример 2: клик мимо кнопки

Аргументы теста
{"id": "page", "tag": "main", "cls": "", "children": [{"id": "list", "tag": "ul", "cls": "items", "children": [{"id": "ad1", "tag": "li", "cls": "card", "children": [{"id": "photo1", "tag": "img", "cls": "photo"}, {"id": "fav1", "tag": "button", "cls": "fav", "children": [{"id": "icon1", "tag": "svg", "cls": "icon"}]}, {"id": "call1", "tag": "button", "cls": "call promo"}]}, {"id": "ad2", "tag": "li", "cls": "card vip", "children": [{"id": "fav2", "tag": "button", "cls": "fav active"}]}]}]}, [["on", "list", "click", "button.fav", "F", null], ["emit", "photo1", "click"]]
Ожидается
[]
Как проверяем Объявите EventTree — тест вызывает её сам. Таймеры и Date в тестах ненастоящие: время идёт по команде теста, поэтому setTimeout на 5 секунд проверяется мгновенно. Тестов 11: 2 открыты выше, остальные приходят после старта подхода. Лимит — 2 с на тест.
class EventTree { constructor(tree); on(nodeId, type, selector, handler); emit(targetId, type) }
событиявсплытиеделегированиедеревья

Источник: на задачах Авито: выдача объявлений. Условие пересказано своими словами.