Как оптимизировать обработку кликов на ячейки большой таблицы в веб-приложении
Что проверяют
Знаете ли вы про делегирование событий и понимаете ли, что при тысячах строк проблема не только в обработчиках, но и в самих узлах.
Как отвечать
Первое и главное: один обработчик на контейнер вместо обработчика на каждую ячейку.
table.addEventListener("click", (e) => {
const cell = e.target.closest("td[data-id]");
if (!cell || !table.contains(cell)) return;
openRow(cell.dataset.id);
});Почему это работает: события всплывают, поэтому достаточно поймать их на таблице. Память не тратится на тысячи слушателей, а строки, добавленные позже, работают сразу, без переподписки. Данные для обработки кладутся в атрибуты, а не ищутся по индексу строки.
Что ещё стоит назвать:
- виртуализация: если строк тысячи, тормозит не обработчик, а сам DOM. Рисуются только видимые строки, остальные заменяются отступами. Это даёт основной выигрыш;
closestвместо проверкиtarget: клик приходит на вложенный элемент, и без подъёма по дереву обработчик просто не сработает;- не считать в обработчике: тяжёлую работу откладывают, а частые события (наведение, прокрутка) ограничивают по частоте;
- пассивные слушатели для прокрутки и касаний, чтобы не блокировать прокрутку;
- в React: обработчик на строке допустим, потому что React сам делегирует события к корню, но при большом списке важнее мемоизация строк и стабильные обработчики, иначе каждая перерисовка создаёт новые функции для всех строк.
Где спотыкаются
Вешают обработчик в цикле по всем ячейкам и удивляются расходу памяти.
Читают e.target напрямую и теряют клики по вложенным элементам вроде иконок.
Оптимизируют обработчики, хотя тормозит отрисовка тысяч узлов, которые нужно виртуализировать.