Длинные списки и виртуализация
В прошлом уроке мы ограничили DOM, сохранив полный набор данных. Теперь сравним три способа представления длинной коллекции: все строки, отдельные страницы и окно около видимой области. Виртуализация может уменьшить число одновременно существующих элементов, но изменяет возможности навигации и поиска. Выбор должен опираться на задачу читателя, а не только на размер дерева.
В advanced/list-lab доступны режимы all, paged и window. Последний является схемой фиксированной геометрии: его строки не содержат интерактивных ссылок. Это намеренное ограничение учебного прототипа, а не готовый доступный каталог. Автор не запускал примеры и не измерял их скорость. Результат урока — обоснованный выбор границы представления.
Что существует при каждом подходе
Полный вариант создаёт тысячу строк со ссылками. Все названия находятся в текущем документе, поэтому обычный поиск браузера может исследовать весь показанный набор. Цена связана с созданием, оформлением и размещением большого представления. Если коллекция невелика и работает удовлетворительно, дополнительная сложность может быть не нужна.
Постраничный вариант показывает пятьдесят строк и предоставляет кнопки перехода. В DOM находится только одна порция, но человек явно управляет её сменой. Для настоящего сайта полезно сохранить номер страницы в URL и обеспечить обычные ссылки. Наш локальный прототип демонстрирует порции в памяти, поэтому ещё не является готовой серверной пагинацией.
Оконный вариант сохраняет видимость длинной полосы прокрутки, но создаёт только строки около текущей позиции. При движении старые элементы удаляются, новые появляются. Таким образом, большая коллекция и полный документ больше не совпадают. Модель оконного представления описана в материале web.dev о виртуальных списках; здесь изучаем её независимо от React и сторонней библиотеки.
Геометрия фиксированной строки
В учебном режиме высота строки равна 48 CSS-пикселям. Для тысячи записей общая расчётная высота получается умножением. Контейнер прокрутки имеет высоту 320 пикселей. Эти размеры выбраны для демонстрации и не являются результатом измерения интерфейса. Если изменить оформление, договор фиксированной высоты нужно пересмотреть.
Основная формула окна из list.js выглядит так:
const start = Math.max(0, Math.floor(viewport.scrollTop / 48) - 2);
const end = Math.min(records.length,
Math.ceil((viewport.scrollTop + viewport.clientHeight) / 48) + 2);
show(start, end);
scrollTop выражает положение внутри контейнера. Деление на высоту даёт приблизительный номер строки. Округление вниз определяет начало, вверх — конец видимого диапазона. Две дополнительные строки с каждой стороны являются учебным запасом. Он уменьшает риск слишком позднего появления соседнего элемента, но увеличивает число одновременно созданных узлов.
Функция show получает полуоткрытый диапазон: начало включено, конец исключён. Ограничение через Math.min не позволяет выйти за конец массива. Каждая строка размещается на координате своего индекса, а общая высота списка сохраняет полосу прокрутки. Без этой высоты удаление невидимых строк превратило бы длинную коллекцию в короткий документ.
Почему переменная высота сложнее
У настоящих карточек заголовок может занять одну или несколько строк. Изображения, язык и увеличение текста также меняют высоту. Тогда деление позиции на одну константу перестаёт правильно определять индекс. Ошибка проявится как пропуски, перекрытия или неожиданный скачок при появлении нового диапазона.
Для переменной высоты потребуется другая модель: известные или измеренные размеры, накопленные смещения и корректировка при изменении содержимого. Такая система значительно сложнее нашего прототипа. Нельзя объявить все карточки фиксированными только потому, что это упрощает формулу, если важные названия перестают быть читаемыми.
В window длинный текст обрезается визуально одной строкой. Это допустимо лишь как обозначенная граница схемы. Для публикационного каталога надо выбрать полноценный способ прочитать название, проверить увеличенный текст и сохранить действие ссылки. Если эти условия не выполнены, выигрыш количества узлов ещё не означает готовое улучшение интерфейса.
Поиск по документу
Попробуйте в ручной сессии найти название далеко за пределами окна, например Учебный материал 0900. В режиме all оно присутствует в DOM. В начальном window такого элемента нет. Браузерный поиск не обязан создавать недостающую виртуальную строку из массива программы. Здесь ограничение связано с представлением, а не с существованием исходных данных.
Постраничный режим также не содержит всех названий сразу. Однако его граница видима пользователю через страницы. Для настоящей библиотеки нужен отдельный поиск по каталогу и обычный доступ к статьям; устройство такого поиска рассматривается в курсе поиска по сайту. Нельзя заменить его обещанием, что строка когда-нибудь появится после прокрутки.
У учебного прототипа есть явная ссылка на полный вариант. Это позволяет сравнить доступность всех названий в одном документе. В настоящем интерфейсе альтернатива должна соответствовать назначению коллекции, размеру данных и требованиям индексирования. Загрузка тысяч ссылок лишь ради запасного режима тоже имеет цену, поэтому решение требуется обосновать.
Клавиатура и дерево доступности
Удаление строки может удалить элемент, на котором находился фокус. Тогда пользователь теряет своё место и дальнейший порядок управления. Поэтому в нашей схеме окна строки текстовые, а контейнер может получать клавиатурный фокус для прокрутки. Это упрощение не показывает полноценное управление виртуальной коллекцией ссылок.
У реального интерактивного списка нужно отдельно определить сохранение активного элемента, переходы между записями, появление нужной строки и восстановление фокуса. Нельзя постоянно пересоздавать всё окно без учёта того, где находится человек. Важны также чтение вспомогательными технологиями и представление общего количества элементов.
Прототип задаёт aria-posinset и aria-setsize текстовым строкам, обозначая позицию в большем наборе. Назначение таких сведений описано в MDN: aria-setsize. Но эти атрибуты не создают отсутствующие названия и не реализуют клавиатурное поведение. Их наличие не доказывает доступность всего решения.
Сохранить DOM, сократить отдельную работу
Иногда задача заключается не в удалении строк, а в пропуске дорогостоящего отображения невидимых разделов. Для этого можно исследовать content-visibility: auto. При таком подходе содержимое остаётся в DOM и сохраняется для возможностей пользовательского агента, включая поиск по странице. Семантика отличается от hidden и описана в MDN: content-visibility.
Это не универсальная замена виртуализации. Создание данных и элементов всё ещё происходит, а особенности геометрии и целевых браузеров требуют проверки. Однако различие полезно для выбора: удаление узлов и пропуск части рендеринга изменяют разные слои. Сначала назовите ограничение, которое действительно нужно уменьшить.
Выберем представление под задачу
Для небольшой подборки часто достаточно полного списка. Для большого учебного каталога понятные страницы с постоянными ссылками могут лучше сохранять навигацию. Для очень большой интерактивной таблицы окно может быть оправдано, если разработан договор доступности и поиска. Ни один вариант не выбирается только по числу исходных записей.
Сравните режимы через функции пользователя: найти дальнее название, открыть запись, вернуться, использовать клавиатуру и увеличить текст. Затем добавьте наблюдение о DOM и работе браузера. Если окно уменьшает узлы, но нарушает нужную операцию, решение требует доработки. Следующий урок расширит условия сравнения: быстрый локальный компьютер не описывает сеть и процессор мобильного читателя.