Live-поиск

Unpoly — это библиотека для создания интерактивных интерфейсов без полной перезагрузки страницы. Одним из распространённых сценариев является live-поиск, когда результаты обновляются динамически по мере ввода текста пользователем. Такой подход снижает нагрузку на сервер и улучшает UX за счёт мгновенного отклика интерфейса.

Основы работы live-поиска

Live-поиск реализуется через комбинацию событий ввода и AJAX-запросов. В Unpoly это делается с помощью атрибутов up-target, up-url и методов up.replace(), up.submit().

Ключевые моменты:

  • up-target указывает, какой элемент страницы будет обновлён при выполнении запроса.
  • up-url задаёт адрес, с которого будут загружаться данные.
  • События input или keyup используются для отправки запроса при изменении текста в поле поиска.

Пример базовой разметки:

<form id="search-form" up-target="#results" up-url="/search" method="get">
    <input type="text" name="query" id="search-input" placeholder="Введите запрос">
</form>

<div id="results">
    <!-- Здесь будут отображаться результаты поиска -->
</div>

В этом примере каждый ввод текста может инициировать отправку формы на сервер, после чего содержимое блока #results обновляется результатами поиска.

Автоподключение Unpoly

Unpoly позволяет автоматически перехватывать формы и ссылки, если указаны атрибуты up-target и up-url. Для live-поиска это означает, что не требуется писать отдельный обработчик событий, если форма настроена корректно.

<input type="text" name="query" placeholder="Поиск" up-target="#results" up-url="/search">
<div id="results"></div>

При вводе текста Unpoly может автоматически выполнять GET-запрос на /search с параметром query и заменять содержимое элемента #results.

Управление частотой запросов

Для live-поиска важно ограничивать количество запросов при быстром вводе текста. В Unpoly предусмотрена функция up.fragment.send() в сочетании с debounce для оптимизации:

const input = document.getElementById('search-input');

input.addEventListener('input', up.util.debounce(() => {
    up.fragment.send(input.form, {
        target: '#results'
    });
}, 300));

Объяснение:

  • up.util.debounce(fn, delay) — создаёт задержку между вызовами функции, предотвращая слишком частые AJAX-запросы.
  • up.fragment.send(form, { target }) — выполняет AJAX-запрос по данным формы и обновляет указанный блок страницы.

Работа с пустым запросом

Обычно при пустом поле поиска нужно очищать результаты. Это можно реализовать так:

input.addEventListener('input', up.util.debounce(() => {
    if (!input.value.trim()) {
        document.getElementById('results').innerHTML = '';
        return;
    }
    up.fragment.send(input.form, { target: '#results' });
}, 300));

Обработка ошибок и индикаторы загрузки

Unpoly позволяет легко отображать состояние загрузки через CSS-классы или события:

up.on('up:request', '#search-form', () => {
    document.getElementById('results').classList.add('loading');
});

up.on('up:response', '#search-form', () => {
    document.getElementById('results').classList.remove('loading');
});

up.on('up:error', '#search-form', (event) => {
    document.getElementById('results').innerHTML = '<p>Ошибка при загрузке данных</p>';
});
  • up:request срабатывает при отправке запроса.
  • up:response — после успешного получения данных.
  • up:error — при ошибке на сервере или сетевой ошибке.

Поддержка истории браузера

Unpoly автоматически обновляет URL при замене содержимого, если указаны соответствующие атрибуты. Это позволяет пользователю возвращаться к предыдущему состоянию поиска через кнопку «Назад» в браузере:

<form id="search-form" up-target="#results" up-url="/search" up-history="true">
    <input type="text" name="query">
</form>

up-history="true" гарантирует, что каждый запрос live-поиска будет добавлен в историю браузера.

Фильтры и дополнительные параметры

Live-поиск часто требует передачи дополнительных параметров, например категорий или сортировки. Это можно сделать, добавив скрытые поля или обработчики:

<form id="search-form" up-target="#results" up-url="/search">
    <input type="text" name="query">
    <select name="category">
        <option value="">Все категории</option>
        <option value="books">Книги</option>
        <option value="movies">Фильмы</option>
    </select>
</form>

Unpoly автоматически сериализует форму и передаст все значения в AJAX-запросе.

Кэширование и оптимизация

Для улучшения производительности можно использовать кэширование:

up.on('up:before-request', '#search-form', (event) => {
    const query = input.value.trim();
    if (queryCache[query]) {
        event.preventDefault();
        up.replace('#results', queryCache[query]);
    }
});

up.on('up:response', '#search-form', (event) => {
    queryCache[input.value.trim()] = event.responseText;
});
  • event.preventDefault() отменяет стандартный запрос.
  • Хранение HTML-результатов в объекте queryCache позволяет повторно использовать их без новых AJAX-вызовов.

Поддержка бесконечной прокрутки

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

up.fragment.on('up:fragment:loaded', '#results', () => {
    const sentinel = document.querySelector('#results .sentinel');
    if (!sentinel) return;

    const observer = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting) {
            up.fragment.send('/search', { target: '#results', append: true, params: { query: input.value } });
        }
    });

    observer.observe(sentinel);
});
  • append: true добавляет новые результаты к существующим.
  • Использование IntersectionObserver позволяет загружать новые данные только при достижении конца списка.

Live-поиск в Unpoly сочетает простую настройку через HTML-атрибуты, мощные AJAX-возможности, поддержку истории браузера и оптимизацию запросов, что делает его эффективным инструментом для динамических интерфейсов.