Реализация live search на JavaScript

Реализация live search на JavaScript

В современном динамичном мире веб-разработок реализация мощной поисковой функциональности является ключевым моментом, позволяющим значительно повысить удобство работы пользователей и упростить навигацию по большим массивам данных. Если вы хотите добавить возможности живого поиска на свой сайт или в веб-приложение, вы попали по адресу. В этой статье мы рассмотрим все тонкости реализации функции живого поиска с помощью JavaScript.

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

Для эффективной работы с данным руководством рекомендуется хорошо знать основы HTML, CSS и JavaScript. Знакомство с манипуляциями с DOM и обработкой событий будет полезным, когда мы погрузимся в детали реализации. Однако даже если вы относительно недавно знакомы с JavaScript или веб-разработкой, руководство построено таким образом, чтобы обеспечить четкие объяснения и пошаговые инструкции, что делает его доступным для учащихся с различным уровнем подготовки.

Теперь, чтобы лучше понять важность и применение этой функциональности, мы создадим в качестве примера очень простой проект, а точнее, приложение для просмотра фильмов, как показано ниже:

Ознакомиться с живой реализацией можно здесь.

В этом проекте мы будем использовать функцию живого поиска для поиска по списку фильмов из базы данных фильмов. Я знаю, что вам уже не терпится погрузиться в работу; мы уже близко к этому. Но сначала давайте узнаем немного больше о том, что такое функциональность живого поиска и какова ее важность.

Важность функции живого поиска

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

  1. Улучшение качества работы пользователей: Живой поиск значительно повышает удобство работы пользователей, обеспечивая интуитивно понятный процесс поиска. Как только пользователь начинает вводить свой запрос, результат поиска обновляется в реальном времени, обеспечивая мгновенную обратную связь и устраняя необходимость в ручном вводе или перезагрузке страницы. Такая интерактивность позволяет сэкономить время и силы пользователей, что делает поиск более эффективным и приятным.
  2. Ускоренный поиск информации: Благодаря “живому” поиску пользователи могут быстро находить нужную информацию, не переходя по нескольким страницам и не ожидая загрузки результатов поиска. По мере ввода текста результаты поиска мгновенно сужаются, отображая соответствующие предложения и избавляя от необходимости вводить полный текст поискового запроса. Благодаря такой скорости и оперативности пользователи могут найти то, что им нужно, за долю времени, которое требуется при использовании традиционных методов поиска.
  3. Увеличение вовлеченности и конверсии: Бесперебойная и оперативная работа живого поиска стимулирует пользователей к более активному взаимодействию с веб-сайтом или веб-приложением. Предоставление мгновенной обратной связи и релевантных предложений поддерживает пользователей, сводя к минимуму вероятность отказов и разочарований. Повышение вовлеченности может привести к повышению конверсии, поскольку пользователи с большей вероятностью будут изучать сайт дальше и конвертировать свои поисковые намерения в действия.
  4. Усовершенствованная фильтрация и уточнение: Функциональность живого поиска часто включает дополнительные возможности, такие как фильтры, предложения и автозаполнение. Эти функции помогают пользователям уточнить поиск и сузить результаты, что позволяет им найти то, что они ищут. Предоставляя такие инструменты, “живой” поиск улучшает качество поиска, а также помогает пользователям обнаружить связанный контент или продукты, о которых они, возможно, даже не подозревали.
  5. Ценные сведения для владельцев сайтов: Функциональность живого поиска позволяет получить ценные сведения о поведении и предпочтениях пользователей. Анализируя поисковые запросы и закономерности, владельцы сайтов могут лучше понять, что ищут их пользователи, выявить популярные тенденции или темы, а также принять обоснованные решения по созданию контента, предложению продуктов или улучшению пользовательского интерфейса. Эти данные позволяют владельцам сайтов адаптировать свои предложения к потребностям пользователей, что приводит к повышению удовлетворенности клиентов и росту бизнеса.

Настройка структуры HTML

Теперь, когда мы имеем полное представление о том, что такое функциональность живого поиска и какова ее важность, давайте рассмотрим, как можно реализовать ее и в своем проекте.

Прежде всего, давайте определим структуру проекта. Для этого проекта нам понадобится всего три файла: HTML, CSS и JavaScript.

Начнем с настройки HTML-структуры нашего проекта: В HTML-файл сначала нужно включить стандартный HTML-шаблон, включающий ссылку и скрипт на наши CSS- и JS-файлы:

<!doctype html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<link rel="stylesheet" href="./live-search.css" />
		<title> </title>
		.
	</head>
	<body>
		<script src="./live-search.js"></script>
	</body>
</html>

Теперь в тег body мы включаем семантические теги header и main. Внутри тега header мы задаем заголовок нашего проекта, который в данном случае представляет собой просто название приложения и иконку видео.

<header>
	<ion-icon name="videocam"></ion-icon>
	<h1>Поиск фильмов</h1>
</header>

Прежде чем перейти к тегу main, в конце тега body включим необходимые теги script для того, чтобы можно было использовать иконки:

<script type="module" src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.js"></script>

Иконки можно найти на сайте Ionicons.

Теперь внутри тега main мы включим наш первый тег div, который будет нашим контейнером строки поиска, а внутри него мы разместим наш тег ввода поиска и иконку поиска:

<div id="search-container">
	<ion-icon name="search-outline"></ion-icon>
	<input type="search" id="search-bar" placeholder="Поиск фильмов..." />
</div>

Затем мы создадим еще один тег div под этим “div”. В нем будут храниться все результаты просмотра фильмов:

<div id="results-container"></div>

Пока оставим его пустым, так как его содержимое будет сгенерировано в разделе JavaScript.

Наконец, в тег main мы включим тег p. Этот тег предназначен для последующего отображения ответа на сообщение об ошибке или пустом сообщении.

<p id="movie-unavailable-txt">

Это все для HTML-файла, а общий код должен выглядеть следующим образом:

<!doctype html>
<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta http-equiv="X-UA-Compatible" content="IE=edge" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <link rel="stylesheet" href="./live-search.css" />
   <title>Функциональные возможности живого поиска</title>
 </head>
 <body>
   <Заголовок>
     <ion-icon name="videocam"></ion-icon>
     <h1>Поиск фильмов</h1>
   </header>
 
   <main>
     <div id="search-container">
       <ion-icon name="search-outline"></ion-icon>
       <input type="search" id="search-bar" placeholder="Поиск фильмов..." />
     </div>
 
     <div id="results-container"></div>
 
     <p id="movie-unavailable-txt">
   </main>
 
   <script src="./live-search.js"></script>
   <script
     type="module"
     src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.esm.js"
   ></script>
   <script
     nomodule
     src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.js"
   ></script>
 </body>
</html>

Теперь, когда мы закончили реализацию HTML-структуры проекта, давайте добавим несколько стилей на страницу.

Добавление стилей на страницу

В этом разделе мы добавим основные стили к различным частям страницы. Поэтому давайте сразу же приступим к работе.

Сначала добавим несколько общих стилей к общей части страницы:

html{ scroll-behavior: smooth; background-color: #111; цвет: белый дым; } *{ margin: 0; padding: 0;
box-sizing: border-box; }

Теперь добавим некоторые стили к разделу заголовка и его содержимому:

header {
	display: flex;
	justify-content: center;
	padding: 25px;
	letter-spacing: 2px;
	position: sticky;
	top: 0%;
	z-index: 2;
	border-bottom: 2px solid;
	background-color: black;
	text-shadow: 3px 3px 5px #fd1d6b;
	box-shadow: 10px 10px 20px -10px #fd1d6b;
}
 
header > ion-icon {
	color: #fd1d6b;
	font-size: 60px;
	position: absolute;
	left: 5%;
}

Далее мы переходим к стилизации контейнера поиска и его содержимого:

#search-container {
	display: flex;
	justify-content: center;
	padding: 20px;
	margin-bottom: 20px;
	position: sticky;
	top: 100px;
}
 
#search-bar {
	border: none;
	ширина: 60%;
	padding: 15px;
	padding-left: 40px;
	border-radius: 15px;
	font-size: 15px;
}
 
#search-container > ion-icon {
	цвет: серый;
	position: relative;
	left: 30px;
	top: 13px;
	z-index: 3;
	font-size: 19px;
}

После этого мы приступаем к созданию стиля results-container, в котором будут храниться все фильмы, полученные из базы данных фильмов:

#results-container {
	border-right: 5px solid #fd1d6b;
	border-left: 5px solid #fd1d6b;
	border-radius: 25px;
	display: flex;
	justify-content: center;
	flex-wrap: wrap;
	width: 90vw;
}

Далее мы добавим стили к movie-unavailable-txt, установив display в none, поскольку пока не хотим, чтобы он был виден:

#movie-unavailable-txt {
	text-align: center;
	интервалмеждубуквами: 2px;
	display: none;
	margin-top: 15%;
	text-shadow: 3px 3px 5px #fd1d6b;
}

Далее мы добавим стили к нескольким элементам, которые еще не были объявлены, но которые мы создадим с помощью javascript. Это карточка фильма, которая будет отображать подробную информацию о фильме, содержащую изображение и название фильма:

.movie-cards {
	padding: 25px;
	max-width: 250px;
	border-radius: 15px;
	display: grid;
	place-items: center;
	box-shadow: 1px 1px 20px -1px #fd1d6b;
	margin: 50px;
}
 
.title {
	margin: 20px auto;
	text-align: center;
	font-size: 1.2rem;
	text-shadow: 3px 3px 5px #fd1d6b;
}
 
.date {
	margin-top: 15px;
	font-size: 0.8rem;
	text-shadow: 3px 3px 5px #fd1d6b;
}
 
.movie-image {
	width: 90%;
	max-width: 400px;
	object-fit: contain;
	border-radius: 5px;
}

Теперь, когда мы закончили со стилизацией страницы, перейдем к самому интересному и важному разделу - реализации Javascript.

Отправка асинхронных поисковых запросов к API базы данных фильмов.

Страница учетных данных API
Страница учетных данных API

Но прежде чем использовать API, необходимо подписаться на него (без использования кредитной карты), чтобы для вас был сгенерирован API-ключ. Это можно сделать на странице [pricing page] (https://rapidapi.com/rapihub-rapihub-default/api/imdb-top-100-movies/pricing) API.

Далее мы переходим в наш пустой файл javascript и вставляем туда скопированный нами код:

const url = 'https://imdb-top-100-movies.p.rapidapi.com/';
const options = {
	method: 'GET',
	headers: {
		'X-RapidAPI-Key': 'СГЕНЕРИРОВАННЫЙ ВАМИ API КЛЮЧ',
		'X-RapidAPI-Host': 'imdb-top-100-movies.p.rapidapi.com',
	},
};
 
try {
	const response = await fetch(url, options);
	const result = await response.text();
	console.log(result);
} catch (error) {
	console.error(error);
}

Теперь, когда мы получили API от базы данных фильмов в наш проект, мы можем использовать ее данные. Для этого объявим несколько необходимых нам переменных и поместим их чуть выше блока try в скопированном нами коде:

const SearchBar = document.getElementById('search-bar');
const resultsContainer = document.getElementById('results-container');
const movieUnavailableTxt = document.getElementById('movie-unavailable-txt');
let movieList;
let searchValue;
let searchedMovies;

Мы подходим к назначению этих переменных, которые мы только что создали, держитесь.

Далее мы вносим некоторые изменения в блок try из скопированного кода, поскольку хотим полностью интегрировать его в наш проект. Итак, для начала нам нужно создать асинхронную функцию:

const fetchMovies = async () => {
	// блок try catch помещается сюда.
};

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

Внутри блока try мы избавимся от строки console.log(result) и изменим переменную result на переменную movieList, которую мы объявили ранее, а также изменим response.text() в этой же строке на response.json(). Это необходимо для того, чтобы данные, полученные в результате вызова API, были представлены в нужном нам формате JSON. Теперь эта строка должна выглядеть следующим образом:

movieList = await response.json();

Теперь, когда мы успешно извлекли фильм из API и вернули набор данных JSON, нам необходимо заполнить этими данными нашу страницу. Для этого мы вызовем функцию renderMovies() и установим в качестве аргумента данные, полученные в результате обращения к API. Не волнуйтесь, мы скоро создадим эту функцию:

renderMovies(movieList);

Теперь создадим функцию renderMovies, которую мы только что вызвали в функции fetchMovies(). С помощью этой функции мы создадим динамический шаблон карточки фильма, стили для которого мы задали ранее в нашем CSS-файле, а в каждом из элементов шаблона мы зададим их содержимое данными, полученными из API, что позволит нам отображать разные фильмы с помощью одного и того же шаблона. Затем мы поместим карточку фильма внутрь элемента resultsContainer. При каждом вызове функции необходимо очищать resultsContainer, устанавливать moviesUnavailableTxt в значение display="none", поскольку мы хотим, чтобы текст не был виден при выводе фильмов на страницу, а также очищать массив moviesReturnedOnSearch, прежде чем поместить в него новые данные, возвращенные из поля ввода поиска:

const renderMovies = (movies) => {
	resultsContainer.innerHTML = ''; // Очищаем существующие фильмы
	movieUnavailableTxt.style.display = 'none'; // Скрыть сообщение "Фильмы не найдены"
	moviesReturnedOnSearch = []; // Очистить массив фильмов, возвращенных при поиске
 
	movies.forEach((movie) => {
		resultsContainer.innerHTML += `
     <div class="movie-cards">
       <img src="${movie.image}" alt="изображение фильма" class="movie-image" />
       <h2 class="title">${movie.title}</h2>
       <p class="plot">${movie.description}
       <p class="date">${movie.year}
     </div>
   `;
 
		moviesReturnedOnSearch.push(movie); // Добавляем фильмы, ставшие результатом поиска, во входное значение поиска
	});
};

Перехват пользовательского ввода и отображение результатов поиска фильмов в реальном времени

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

Для перехвата пользовательского ввода мы будем использовать слушатель событий input и привяжем его к элементу searchBar. Мы используем именно этот приемник событий, поскольку он фиксирует все действия внутри поисковой строки - от ввода до очистки и вставки, что как раз то, что нам нужно. Поэтому давайте создадим его:

searchBar.addEventListener('input', (event) => {
	// живой код функциональности
});

Итак, мы подключили к строке поиска слушатель события input, который будет воспринимать любой ввод от пользователя. Во втором параметре мы добавили обработчик события - функцию, которая будет вызываться каждый раз, когда в строку поиска будет введен хоть один элемент. Теперь внутри этой функции мы создадим код, который будет обрабатывать живой поиск.

Первое, что нам нужно сделать внутри функции поиска, - это отредактировать значение ввода, полученное от пользователя, и установить его во все строчные буквы, а также избавиться от лишних пробелов:

searchValue = event.target.value.trim().toLowerCase();

После этого мы приступаем к фильтрации фильмов на странице по названию, основываясь на поисковом вводе пользователя, проверяя, включает ли в себя название фильма, введенное пользователем, любое из названий фильмов в данных movieList, а также устанавливая названия фильмов в нижний регистр, чтобы соответствовать вводу пользователя:

const filteredMovies = movieList.filter((movie) => movie.title.toLowerCase().includes(searchValue));

Далее мы выводим результаты поиска фильмов в реальном времени, отображая название фильма, соответствующее символам, которые пользователь набрал в строке поиска, еще раз вызывая функцию renderMovies() и устанавливая в качестве аргументов значения переменной filtered Movies.

renderMovies(filteredMovies);

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

Обработка пустых или ошибочных ответов

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

Для обработки ошибок или пустых ответов необходимо обеспечить четкую обратную связь с пользователем. При этом, поскольку это довольно легкое приложение, нам не нужно беспокоиться о большом количестве ошибок, поскольку мы будем иметь дело только с ошибками, возникающими при работе с API. Например, сервисы API могут быть временно отключены, или приложение просто превысило лимит запросов. Для обработки этой ошибки нам достаточно установить display элемента movieUnavailableTxt в block и установить innerHTML для отображения сообщения об ошибке пользователю и поместить его внутри блока catch функции fetchMovies(). Теперь блок catch выглядит следующим образом:

catch (error) {
 movieUnavailableTxt.innerHTML = 'При получении фильмов произошла ошибка. <br /> Пожалуйста, повторите попытку позже.'
 movieUnavailableTxt.style.display = "block";
 console.error(error);
}

Теперь, когда мы закончили работу с ответом на ошибку, перейдем к работе с пустым ответом. Если фильм, который ищет пользователь, не совпадает ни с одним из фильмов на странице, то необходимо предупредить пользователя о том, что искомый фильм недоступен. Для этого сначала нужно проверить содержимое массива moviesReturnedOnSearch, который мы объявили ранее, и если длина массива меньше или равна 0, то установить display элемента movieUnavailableTxt в block и установить innerHTML в пустое сообщение ответа, которое мы хотим отобразить, как показано ниже:

if (moviesReturnedOnSearch.length <= 0) {
	movieUnavailableTxt.innerHTML = 'OOPS! <br/><br/> Фильм недоступен';
	movieUnavailableTxt.style.display = 'block'; // Показать сообщение "Фильмы не найдены", если ни один фильм не соответствует поиску
}

Этот блок if мы поместим непосредственно перед закрывающей скобкой обработчика события searchBar.

Повышение производительности поиска с помощью кэширования

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

Чтобы реализовать кэширование в нашем проекте, сначала нужно определить, какой элемент необходимо кэшировать, и в данном случае это будет значение переменной movieList, которая представляет собой данные, полученные нами в результате API-запроса fetch в формате JSON. Кэширование этого элемента позволит нам использовать данные API без дополнительного запроса fetch, даже при перезагрузке страницы. Но для данного проекта мы установим срок действия кэшированных данных в 6 часов, то есть страница будет запрашивать API только раз в 6 часов, а не при каждой перезагрузке страницы. Это необходимо для того, чтобы страница могла сохранять свежесть и актуальность своих данных, а количество запросов к API было минимальным.

Возвращаясь к нашему коду, отметим, что теперь нам необходимо сохранить данные JSON в локальном хранилище браузера, но для этого нужно сначала превратить их в строку и задать имя ключа, который будет использоваться для идентификации данных в локальном хранилище. Зададим его как movieData, как показано ниже:

localStorage.setItem('moviedata', JSON.stringify(movieList));

Следующее, что нам нужно сделать, это сохранить текущую дату и время в локальном хранилище:

localStorage.setItem('cacheTimestamp', Date.now());

Здесь хранится текущая дата и время в миллисекундах с ключевым именем cacheTimeStamp.

Эти две строки кода мы поместим в блок try функции fetchMovies(), прямо под переменной movieList.

Далее, вне функции fetchMovies(), чуть ниже функции renderMovies(), мы установим время истечения кэшированных данных равным 6 часам в миллисекундах:

const expirationDuration = 21600000; // 6 часов в миллисекундах

После этого нам необходимо получить обратно cacheTimestamp, который мы ранее установили в локальном хранилище:

const cacheTimestamp = localStorage.getItem('cacheTimestamp');

Теперь мы проверим, не истек ли срок действия кэшированных данных или они недоступны, т.е. еще не были сохранены. Если это так, то мы сделаем новый запрос fetch к API, вызвав функцию fetchMovies(). С другой стороны, если кэшированные данные присутствуют и срок их хранения еще не истек, мы используем их для отображения фильмов на странице, а не делаем новый запрос fetch. Для этого мы извлекаем кэшированные данные о фильмах и разбираем их в формат JSON, а затем вызываем функцию render с аргументом, равным данным, полученным из кэша.

// Проверяем, не истек ли срок действия кэша или не доступны ли данные
if (!cacheTimestamp || Date.now() - parseInt(cacheTimestamp) > expirationDuration) {
	// Срок действия кэша истек или данные недоступны, выполните повторную выборку фильмов
	fetchMovies();
} else {
	// Используем кэшированные данные о фильмах
	movieList = JSON.parse(localStorage.getItem('moviedata'));
	renderMovies(movieList);
}

В операторе if переменная !cacheTimestamp проверяет, является ли переменная cacheTimestamp фальшивой, то есть равна ли она null, undefined, 0, false или пустой строке. Если cacheTimestamp равна false, то это означает, что в кэше не хранится ни одной существующей временной метки. Функция Date.now() - parseInt(cacheTimestamp) вычисляет разницу во времени между текущей временной меткой и разобранным целочисленным значением cacheTimestamp. Таким образом, мы просто говорим: “Значение текущего времени минус значение времени, которое мы ранее хранили в кэше, больше ли оно, чем установленное нами время истечения? Если да, то давайте снова получать фильмы из API, а если нет, то просто воспользуйтесь кэшированными данными”.

Вот так мы кэшируем данные для повторного использования, а не выполняем запрос fetch при каждом вводе пользователя или при каждой перезагрузке страницы. Как видите, это значительно оптимизирует работу приложения, так как предотвращает медленный рендеринг фильма, который может происходить из-за медленной сети.

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

const url = 'https://imdb-top-100-movies.p.rapidapi.com/';
const options = {
	method: 'GET',
	headers: {
		'X-RapidAPI-Key': 'Ваш сгенерированный API-ключ',
		'X-RapidAPI-Host': 'imdb-top-100-movies.p.rapidapi.com',
	},
};
 
const SearchBar = document.getElementById('search-bar');
const resultsContainer = document.getElementById('results-container');
const movieUnavailableTxt = document.getElementById('movie-unavailable-txt');
let movieList;
let searchValue;
let moviesReturnedOnSearch;
 
// Функция для получения фильмов из API
const fetchMovies = async () => {
	try {
		const response = await fetch(url, options);
		movieList = await response.json();
 
		// Хранение данных о фильмах в хранилище браузера
		localStorage.setItem('moviedata', JSON.stringify(movieList));
		localStorage.setItem('cacheTimestamp', Date.now()); // Обновление временной метки кэша
 
		// Рендеринг фильмов на странице
		renderMovies(movieList);
	} catch (error) {
		movieUnavailableTxt.innerHTML =
			'Произошла ошибка при получении фильмов. <br /> Пожалуйста, повторите попытку позже.';
		movieUnavailableTxt.style.display = 'block';
		console.error(error);
	}
};
 
// Функция для вывода фильмов на страницу
const renderMovies = (movies) => {
	resultsContainer.innerHTML = ''; // Очистка существующих фильмов
	movieUnavailableTxt.style.display = 'none'; // Скрыть сообщение "Фильмы не найдены"
	moviesReturnedOnSearch = []; // Очистить массив фильмов, возвращенных при поиске
 
	movies.forEach((movie) => {
		resultsContainer.innerHTML += `
     <div class="movie-cards">
       <img src="${movie.image}" alt="изображение фильма" class="movie-image" />
       <h2 class="title">${movie.title}</h2>
       <p class="plot">${movie.description}
       <p class="date">${movie.year}
     </div>
   `;
 
		moviesReturnedOnSearch.push(movie); // Добавляем фильмы, ставшие результатом поиска, во входное значение поиска
	});
};
 
const cacheTimestamp = localStorage.getItem('cacheTimestamp');
const expirationDuration = 21600000; // 6 часов в миллисекундах
 
// Проверяем, истек ли срок действия кэша или данные недоступны
if (!cacheTimestamp || Date.now() - parseInt(cacheTimestamp) > expirationDuration) {
	// Срок действия кэша истек или данные недоступны, выполните повторную выборку фильмов
	fetchMovies();
} else {
	// Используем кэшированные данные о фильмах
	movieList = JSON.parse(localStorage.getItem('moviedata'));
	renderMovies(movieList);
}
 
// Слушатель и обработчик событий для ввода строки поиска
searchBar.addEventListener('input', (event) => {
	searchValue = event.target.value.trim().toLowerCase();
 
	// Отфильтровать фильмы на основе поисковых данных
	const filteredMovies = movieList.filter((movie) =>
		movie.title.toLowerCase().includes(searchValue),
	);
 
	// Отображение отфильтрованных фильмов на странице
	renderMovies(filteredMovies);
 
	if (moviesReturnedOnSearch.length <= 0) {
		movieUnavailableTxt.style.display = 'block'; // Показываем сообщение "Фильмы не найдены", если ни один фильм не соответствует поиску
	}
});

Заключение

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

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

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

Счастливого кодирования!

Связаться со мной

@gorlovfrontend