載說明:原創(chuàng)不易,未經(jīng)授權(quán),謝絕任何形式的轉(zhuǎn)載
在當(dāng)今充滿活力的網(wǎng)絡(luò)開發(fā)領(lǐng)域中,實現(xiàn)強大的搜索功能是一個關(guān)鍵特性,可以極大地增強用戶體驗,并使瀏覽大型數(shù)據(jù)集變得輕松自如。如果您想要為您的網(wǎng)站或網(wǎng)絡(luò)應(yīng)用程序添加實時搜索功能,那么您來對地方了。本篇全面的文章將探討使用JavaScript實現(xiàn)實時搜索功能的方方面面。
無論您是經(jīng)驗豐富的開發(fā)人員還是剛開始編碼之旅的新手,本文旨在為您提供一般編碼知識和工具,以便將實時搜索功能融入到您的項目中。通過本指南的學(xué)習(xí),您將對相關(guān)概念和技術(shù)有扎實的理解,從而能夠創(chuàng)建響應(yīng)式和交互式的搜索功能,實現(xiàn)用戶輸入時動態(tài)更新的效果。
為了有效地跟隨本指南,建議您對HTML、CSS和JavaScript的基本知識有扎實的理解。熟悉DOM操作和事件處理將有助于我們深入了解實現(xiàn)細節(jié)。然而,即使您對JavaScript或Web開發(fā)相對較新,本指南的結(jié)構(gòu)也旨在提供清晰的解釋和逐步的說明,使其適用于不同技能水平的學(xué)習(xí)者。
現(xiàn)在,為了更好地理解這個功能的重要性和使用方法,我們將創(chuàng)建一個非常基本的項目作為示例;更具體地說,一個如下所示的電影應(yīng)用程序:
您可以在這里查看實時實施情況。
https://search-movies-live.netlify.app/
在這個項目中,我們將利用實時搜索功能來搜索電影數(shù)據(jù)庫中的電影列表。我知道你迫不及待地想要開始了,我們馬上就會開始。但首先,讓我們更多地了解一下實時搜索功能及其重要性。
在當(dāng)今數(shù)字化的環(huán)境中,實時搜索功能變得至關(guān)重要,滿足了高效信息檢索的需求,提升了整體用戶體驗。通過在用戶輸入時實時更新搜索結(jié)果,實時搜索提供即時反饋,便于快速獲取相關(guān)信息。這種動態(tài)交互式的搜索功能帶來了許多好處,使用戶和網(wǎng)站所有者受益。
既然我們已經(jīng)完全了解了實時搜索功能以及它的重要性,那么讓我們深入探討一下如何在您自己的項目中實現(xiàn)這個功能。
首先,讓我們建立項目的結(jié)構(gòu)。對于這個項目,我們只需要三個文件,即HTML、CSS和JavaScript文件。
現(xiàn)在讓我們開始設(shè)置項目的HTML結(jié)構(gòu):在HTML文件中,我們首先需要包含我們的標(biāo)準(zhǔn)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>
現(xiàn)在在 body 標(biāo)簽中,我們包含了 header 和 main 語義標(biāo)簽。在 header 標(biāo)簽內(nèi),我們設(shè)置了項目的標(biāo)題部分,這里只包括應(yīng)用程序的名稱和一個視頻圖標(biāo)。
<header>
<ion-icon name="videocam"></ion-icon>
<h1>Search Movies</h1>
</header>
在我們繼續(xù)講解 main 標(biāo)簽之前,在 body 標(biāo)簽的末尾,讓我們包含所需的 script 標(biāo)簽,以便能夠使用這些圖標(biāo):
<script
type="module"
src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"
></script>
<script
nomodule
src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"
></script>
您可以在Ionicons網(wǎng)站上找到這些圖標(biāo)。
https://ionic.io/ionicons
現(xiàn)在,在 main 標(biāo)簽內(nèi),我們將包含我們的第一個 div 標(biāo)簽,這將是我們的搜索欄容器,在其中,我們放置我們的搜索輸入標(biāo)簽和一個搜索圖標(biāo):
<div id="search-container">
<ion-icon name="search-outline"></ion-icon>
<input type="search" id="search-bar" placeholder="Search movies..." />
</div>
然后,我們將在這個“div”下面創(chuàng)建另一個 div 標(biāo)簽。這將包含所有電影數(shù)據(jù)的結(jié)果:
<div id="results-container"></div>
我們暫時將其留空,因為其內(nèi)容將在JavaScript部分生成。
最后,在 main 標(biāo)簽中,我們將包含一個 p 標(biāo)簽。這個標(biāo)簽只是為了在稍后向用戶顯示錯誤或空消息的響應(yīng)。
<p id="movie-unavailable-txt"></p>
這就是HTML文件的全部內(nèi)容,整體代碼應(yīng)該是這樣的:
<!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>Live Search Functionality</title>
</head>
<body>
<header>
<ion-icon name="videocam"></ion-icon>
<h1>Search Movies</h1>
</header>
<main>
<div id="search-container">
<ion-icon name="search-outline"></ion-icon>
<input type="search" id="search-bar" placeholder="Search movies..." />
</div>
<div id="results-container"></div>
<p id="movie-unavailable-txt"></p>
</main>
<script src="./live-search.js"></script>
<script
type="module"
src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"
></script>
<script
nomodule
src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"
></script>
</body>
</html>
既然我們已經(jīng)完成了項目的HTML結(jié)構(gòu)的實現(xiàn),那么讓我們給頁面添加一些樣式。
在這個部分,我們將為頁面的各個部分添加基本的樣式。所以讓我們開始吧。首先,讓我們?yōu)轫撁娴恼w部分添加一些常規(guī)樣式:
html{
scroll-behavior: smooth;
background-color: #111;
color: whitesmoke;
}
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
現(xiàn)在讓我們?yōu)轭^部部分及其內(nèi)容添加一些樣式:
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%;
}
接下來,我們開始對搜索容器及其內(nèi)容進行樣式設(shè)置:
#search-container{
display: flex;
justify-content: center;
padding: 20px;
margin-bottom: 20px;
position: sticky;
top: 100px;
}
#search-bar{
border: none;
width: 60%;
padding: 15px;
padding-left: 40px;
border-radius: 15px;
font-size: 15px;
}
#search-container>ion-icon{
color: gray;
position: relative;
left: 30px;
top: 13px;
z-index: 3;
font-size: 19px;
}
之后,我們繼續(xù)設(shè)計將來從電影數(shù)據(jù)庫中獲取的所有電影的 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 設(shè)置為 none ,因為我們暫時不希望它可見
#movie-unavailable-txt{
text-align: center;
letter-spacing: 2px;
display: none;
margin-top: 15%;
text-shadow: 3px 3px 5px #fd1d6b;
}
接下來,我們將為一些尚未聲明但將通過JavaScript創(chuàng)建的元素添加一些樣式。這是電影卡片,將顯示電影的詳細信息,包括電影圖片和標(biāo)題:
.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;
}
既然我們已經(jīng)完成了頁面的樣式,那么讓我們繼續(xù)進行最有趣和最重要的部分,即Javascript的實現(xiàn)。
在本節(jié)中,我們將調(diào)用我們選擇的電影數(shù)據(jù)庫API來填充我們的頁面,展示各種電影。在這種情況下,我將使用RapidAPI Hub中的IMDb Top 100 Movies免費電影API。在API頁面中,我們選擇要使用的特定數(shù)據(jù),然后復(fù)制頁面右側(cè)提供的javascript(fetch)代碼,如下所示:
https://rapidapi.com/rapihub-rapihub-default/api/imdb-top-100-movies
首先,在您使用API之前,請先訂閱它(無需信用卡),以便為您生成一個API密鑰。您可以在API的定價頁面上進行訂閱。
繼續(xù),我們進入我們的空的JavaScript文件,并將我們復(fù)制的代碼粘貼進去:
const url = "https://imdb-top-100-movies.p.rapidapi.com/";
const options = {
method: "GET",
headers: {
"X-RapidAPI-Key": "YOUR GENERATED API KEY",
"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);
}
既然我們已經(jīng)將電影數(shù)據(jù)庫的API引入到我們的項目中,我們可以使用它的數(shù)據(jù)了。接下來,我們將聲明一些我們需要的變量,并將它們放在我們復(fù)制的代碼中 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;
我們正在接近剛剛創(chuàng)建的這些變量的目的,堅持住。
接下來,我們對復(fù)制的代碼中的 try 塊進行一些更改,因為我們希望將其與我們的項目完全集成。所以首先,我們需要創(chuàng)建一個異步函數(shù):
const fetchMovies = async () => {
// try catch block goes in here.
};
在這個函數(shù)內(nèi)部,我們將放置從我們復(fù)制的代碼中的整個 try catch 塊,以便我們可以進行異步API調(diào)用。
在 try 塊內(nèi),我們將刪除 console.log(result) 行,并將 result 變量更改為先前聲明的 movieList 變量,并將同一行中的 response.text() 更改為 response.json() 。這樣,我們從API調(diào)用中接收到的數(shù)據(jù)將以 JSON 格式呈現(xiàn),這是我們所需的。因此,該行現(xiàn)在應(yīng)該是這樣的
movieList = await response.json();
現(xiàn)在我們已經(jīng)成功從API中獲取了電影并返回了我們的數(shù)據(jù)集,我們需要將這些數(shù)據(jù)填充到我們的頁面中。為此,我們將調(diào)用 renderMovies() 函數(shù),并將參數(shù)設(shè)置為從API調(diào)用中獲取的數(shù)據(jù)。不用擔(dān)心,我們很快就會創(chuàng)建這個函數(shù):
renderMovies(movieList);
現(xiàn)在讓我們創(chuàng)建 renderMovies 函數(shù),這個函數(shù)剛剛在 fetchMovies() 函數(shù)中調(diào)用過,這個函數(shù)將用于創(chuàng)建動態(tài)電影卡片模板,我們之前在CSS文件中設(shè)置了樣式,模板中的每個元素的內(nèi)容都將設(shè)置為從API獲取的數(shù)據(jù),這樣我們就可以使用相同的模板渲染不同的電影。然后,我們將把電影卡片放在 resultsContainer 元素中。每次調(diào)用函數(shù)時,我們需要清除 resultsContainer ,并將 moviesUnavailableTxt 設(shè)置為 display="none" ,因為我們希望在渲染電影到頁面時文本不可見,同時清除 moviesReturnedOnSearch 數(shù)組,然后將從搜索輸入字段返回的新數(shù)據(jù)設(shè)置到其中。
const renderMovies = (movies) => {
resultsContainer.innerHTML = ""; // Clear the existing movies
movieUnavailableTxt.style.display = "none"; // Hide the "No movies found" message
moviesReturnedOnSearch = []; // Clear the movies returned on search array
movies.forEach((movie) => {
resultsContainer.innerHTML += `
<div class="movie-cards">
<img src="${movie.image}" alt="movie image" class="movie-image" />
<h2 class="title">${movie.title}</h2>
<p class="plot">${movie.description}</p>
<p class="date">${movie.year}</p>
</div>
`;
moviesReturnedOnSearch.push(movie); // Add the movies that are a result to the search input value
});
};
現(xiàn)在我們已經(jīng)將所有電影數(shù)據(jù)加載到我們的頁面上,接下來是真正有趣的部分,我們要實現(xiàn)實時搜索功能,所以,不浪費時間,讓我們開始吧。
為了捕獲用戶輸入,我們將使用 input 事件監(jiān)聽器,并將其鏈接到 searchBar 元素。我們使用這個特定的事件監(jiān)聽器,因為它可以捕獲搜索框內(nèi)的每一個活動,包括輸入、清除和粘貼,這正是我們想要的。所以讓我們繼續(xù)創(chuàng)建它:
searchBar.addEventListener("input", (event) => {
// live functionality code
});
好的,現(xiàn)在我們已經(jīng)將事件監(jiān)聽器鏈接到搜索欄,以便監(jiān)聽用戶的任何輸入。在第二個參數(shù)中,我們添加了事件處理程序,這是每當(dāng)搜索欄有輸入時將被調(diào)用的函數(shù)。現(xiàn)在,在該函數(shù)內(nèi)部,我們將編寫處理實時搜索的代碼。
在搜索功能中,我們需要做的第一件事是編輯從用戶那里獲取到的輸入值,并將其轉(zhuǎn)換為全小寫,同時去除任何不必要的空格:
searchValue = event.target.value.trim().toLowerCase();
在那之后,我們繼續(xù)根據(jù)用戶的搜索輸入,通過檢查用戶輸入的電影標(biāo)題是否包含在 movieList 數(shù)據(jù)中的任何電影標(biāo)題中,并將電影標(biāo)題設(shè)置為小寫以與用戶輸入匹配,來在頁面上按標(biāo)題篩選電影
const filteredMovies = movieList.filter( (movie) => movie.title.toLowerCase().includes(searchValue) );
接下來,我們將通過再次調(diào)用 renderMovies() 函數(shù),并將參數(shù)設(shè)置為 filtered Movies 變量的值,來顯示與用戶在搜索欄中輸入的字符匹配的電影標(biāo)題的實時搜索結(jié)果。
renderMovies(filteredMovies);
通過調(diào)用此函數(shù),它僅將與搜索欄中鍵入的字符匹配的電影渲染到頁面上,使用函數(shù)中提供的電影卡片模板,同時將每個匹配的電影添加到 moviesReturnedOnSearch 數(shù)組中,以便我們可以跟蹤每個字符輸入的匹配搜索值的電影數(shù)量。在處理空響應(yīng)錯誤時,這將非常有用,現(xiàn)在我們將進入這個部分。
在任何應(yīng)用程序中,有效處理空或錯誤的響應(yīng)至關(guān)重要。在這種情況下,這些情景可能發(fā)生在搜索查詢沒有結(jié)果或API請求存在問題時。
處理錯誤或空響應(yīng)時,向用戶提供清晰的反饋是至關(guān)重要的。話雖如此,由于這是一個相對簡單的應(yīng)用程序,我們不必過多擔(dān)心錯誤,因為我們只需要處理由API引起的錯誤。例如,API服務(wù)可能暫時不可用,或者應(yīng)用程序可能剛剛超過了請求限制。為了處理這個錯誤,我們只需要將 movieUnavailableTxt 元素的 display 設(shè)置為 block ,并將 innerHTML 設(shè)置為向用戶顯示錯誤消息,并將其放置在 fetchMovies() 函數(shù)的 catch 塊中。因此,現(xiàn)在 catch 塊的代碼如下所示:
catch (error) {
movieUnavailableTxt.innerHTML = 'An error occurred while fetching movies. <br /> Please try again later.';
movieUnavailableTxt.style.display = "block";
console.error(error);
}
現(xiàn)在我們已經(jīng)處理完錯誤響應(yīng),接下來我們要處理空響應(yīng)。如果用戶搜索的電影與頁面上的任何電影都不匹配,我們需要向用戶提示所搜索的電影不可用。為了做到這一點,首先我們需要檢查之前聲明的 moviesReturnedOnSearch 數(shù)組的內(nèi)容,如果數(shù)組的長度小于或等于0,我們將設(shè)置 movieUnavailableTxt 元素的 display 為 block ,并將 innerHTML 設(shè)置為空響應(yīng)消息,如下所示:
if (moviesReturnedOnSearch.length <= 0) {
movieUnavailableTxt.innerHTML = "OOPS! <br/><br/> Movie not available";
movieUnavailableTxt.style.display = "block"; // Show the "No movies found" message if no movies match the search
}
我們將把這個 if 塊放在 searchBar 事件處理程序的閉合括號之前。
在使用API實現(xiàn)實時搜索功能時,提高性能的一種有效技術(shù)是緩存。緩存涉及存儲先前獲取的搜索結(jié)果,并在再次請求相同的搜索查詢時重復(fù)使用它們。這可以顯著減少API調(diào)用的次數(shù),有助于防止超過API的請求限制,并改善搜索功能的響應(yīng)速度以及網(wǎng)站的加載時間。
要在我們的項目中實現(xiàn)緩存,首先,我們需要確定哪些項目需要被緩存,而在這種情況下,那將是 movieList 變量的值,它是我們從 fetch API請求中得到的 JSON 格式的數(shù)據(jù)。通過緩存這個項目,我們將能夠在頁面重新加載時使用API的數(shù)據(jù),而無需進行額外的 fetch 請求。但是對于這個項目,我們將為我們的緩存數(shù)據(jù)設(shè)置一個過期時間,為6小時,這意味著頁面每6小時只會進行一次API請求,而不是在每次頁面重新加載時都進行請求。這樣做是為了保持頁面的數(shù)據(jù)新鮮和最新,同時將API請求保持在最低限度。
回到我們的代碼中,現(xiàn)在我們需要將數(shù)據(jù)存儲在瀏覽器的本地存儲中,但為了做到這一點,我們需要首先將其轉(zhuǎn)換為一個 string ,并設(shè)置一個鍵名,用于在本地存儲中標(biāo)識數(shù)據(jù)。讓我們將其設(shè)置為 movieData ,如下所示:
localStorage.setItem("moviedata", JSON.stringify(movieList));
接下來我們需要將當(dāng)前日期和時間存儲在本地存儲中:
localStorage.setItem("cacheTimestamp", Date.now());
這將當(dāng)前日期和時間以毫秒為單位存儲,鍵名為 cacheTimeStamp 。
我們將把這兩行代碼放在 try 塊的 fetchMovies() 函數(shù)中,就在 movieList 變量的下方。
接下來,在 fetchMovies() 函數(shù)之外,緊接著 renderMovies() 函數(shù)的下方,我們將把緩存數(shù)據(jù)的過期時間設(shè)置為6小時(以毫秒為單位)
const expirationDuration = 21600000; // 6 hours in milliseconds
之后,我們需要取回之前在本地存儲中設(shè)置的 cacheTimestamp :
const cacheTimestamp = localStorage.getItem("cacheTimestamp");
現(xiàn)在,我們將檢查緩存數(shù)據(jù)是否已過期或不可用,這意味著它尚未被存儲。如果是這種情況,我們將通過調(diào)用 fetch 函數(shù)向API發(fā)出新的請求。另一方面,如果緩存數(shù)據(jù)存在且尚未過期,我們將使用它來渲染頁面上的電影,而不是再次發(fā)出新的 fetch 請求。我們通過檢索緩存的電影數(shù)據(jù)并將其解析回 JSON 格式來使用,然后將參數(shù)設(shè)置為從緩存中獲取的數(shù)據(jù),調(diào)用 render 函數(shù)來實現(xiàn)這一點。
// Check if cache has expired or data is not available
if (
!cacheTimestamp ||
Date.now() - parseInt(cacheTimestamp) > expirationDuration
) {
// Cache expired or data not available, fetch movies again
fetchMovies();
} else {
// Use cached movie data
movieList = JSON.parse(localStorage.getItem("moviedata"));
renderMovies(movieList);
}
在 if 語句中, !cacheTimestamp 檢查 cacheTimestamp 變量是否為假值,意味著它可以是 null 、 undefined 、0、 false 或空字符串。如果 cacheTimestamp 為假,表示沒有存儲現(xiàn)有緩存時間戳。 Date.now() - parseInt(cacheTimestamp) 計算當(dāng)前時間戳與解析的 cacheTimestamp 的整數(shù)值之間的時間差。簡單來說,這就是說:“當(dāng)前時間的值減去我們之前存儲在緩存中的時間的值,是否大于我們設(shè)置的過期時間?如果是,就從API中重新獲取電影數(shù)據(jù);如果不是,就使用緩存的數(shù)據(jù)。”
就是這樣,這就是我們?nèi)绾螌?shù)據(jù)緩存起來以便重復(fù)使用,而不是在每次用戶輸入或每次頁面重新加載時發(fā)起請求。正如你所看到的,這將極大地優(yōu)化應(yīng)用程序的性能,因為它可以防止由于網(wǎng)絡(luò)慢而導(dǎo)致的電影渲染緩慢。
我們已經(jīng)完成了我們的小電影應(yīng)用程序中展示實時搜索功能的所有特性的實現(xiàn)。以下是該應(yīng)用程序的整體JavaScript代碼:
const url = "https://imdb-top-100-movies.p.rapidapi.com/";
const options = {
method: "GET",
headers: {
"X-RapidAPI-Key": "Your Generated API Key",
"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;
// Function to fetch movies from the API
const fetchMovies = async () => {
try {
const response = await fetch(url, options);
movieList = await response.json();
// Storing the Movie Data in browser storage
localStorage.setItem("moviedata", JSON.stringify(movieList));
localStorage.setItem("cacheTimestamp", Date.now()); // Update cache timestamp
// Render the movies on the page
renderMovies(movieList);
} catch (error) {
movieUnavailableTxt.innerHTML =
"An error occurred while fetching movies. <br /> Please try again later.";
movieUnavailableTxt.style.display = "block";
console.error(error);
}
};
// Function to render movies on the page
const renderMovies = (movies) => {
resultsContainer.innerHTML = ""; // Clear the existing movies
movieUnavailableTxt.style.display = "none"; // Hide the "No movies found" message
moviesReturnedOnSearch = []; // Clear the movies returned on search array
movies.forEach((movie) => {
resultsContainer.innerHTML += `
<div class="movie-cards">
<img src="${movie.image}" alt="movie image" class="movie-image" />
<h2 class="title">${movie.title}</h2>
<p class="plot">${movie.description}</p>
<p class="date">${movie.year}</p>
</div>
`;
moviesReturnedOnSearch.push(movie); // Add the movies that are a result to the search input value
});
};
const cacheTimestamp = localStorage.getItem("cacheTimestamp");
const expirationDuration = 21600000; // 6 hours in milliseconds
// Check if cache has expired or data is not available
if (
!cacheTimestamp ||
Date.now() - parseInt(cacheTimestamp) > expirationDuration
) {
// Cache expired or data not available, fetch movies again
fetchMovies();
} else {
// Use cached movie data
movieList = JSON.parse(localStorage.getItem("moviedata"));
renderMovies(movieList);
}
// Event listener and handler for search bar input
searchBar.addEventListener("input", (event) => {
searchValue = event.target.value.trim().toLowerCase();
// Filter movies based on search input
const filteredMovies = movieList.filter((movie) =>
movie.title.toLowerCase().includes(searchValue),
);
// Render the filtered movies on the page
renderMovies(filteredMovies);
if (moviesReturnedOnSearch.length <= 0) {
movieUnavailableTxt.style.display = "block"; // Show the "No movies found" message if no movies match the search
}
});
在本指南中,我們探討了使用API在JavaScript中實現(xiàn)實時搜索功能的方法。按照所述步驟,您可以創(chuàng)建一個動態(tài)搜索體驗,當(dāng)用戶在搜索欄中輸入時,可以提供實時結(jié)果。
通過在您的網(wǎng)站上實現(xiàn)實時搜索功能,您可以增強用戶參與度,提高您的網(wǎng)站或應(yīng)用程序的可用性。用戶將欣賞能夠快速方便地找到相關(guān)信息,而無需重新加載頁面。
通過這個指南所獲得的知識,您已經(jīng)具備了在JavaScript中有效實現(xiàn)實時搜索功能的能力。擁抱動態(tài)搜索的力量,創(chuàng)造一個無縫的用戶體驗,給人留下深刻的印象。
由于文章內(nèi)容篇幅有限,今天的內(nèi)容就分享到這里,文章結(jié)尾,我想提醒您,文章的創(chuàng)作不易,如果您喜歡我的分享,請別忘了點贊和轉(zhuǎn)發(fā),讓更多有需要的人看到。同時,如果您想獲取更多前端技術(shù)的知識,歡迎關(guān)注我,您的支持將是我分享最大的動力。我會持續(xù)輸出更多內(nèi)容,敬請期待。
來了~歡迎轉(zhuǎn)發(fā)、收藏,記得點個贊唄。^_^
無論當(dāng)前 JavaScript 代碼是內(nèi)嵌還是在外鏈文件中,頁面的下載和渲染都必須停下來等待腳本執(zhí)行完成。JavaScript 執(zhí)行過程耗時越久,瀏覽器等待響應(yīng)用戶輸入的時間就越長。瀏覽器在下載和執(zhí)行腳本時出現(xiàn)阻塞的原因在于,腳本可能會改變頁面或 JavaScript 的命名空間,它們對后面頁面內(nèi)容造成影響。一個典型的例子就是在頁面中使用document.write()。例如清單 1。
清單 1 JavaScript 代碼內(nèi)嵌示例
當(dāng)瀏覽器遇到<script>標(biāo)簽時,當(dāng)前 HTML 頁面無從獲知 JavaScript 是否會向<p> 標(biāo)簽添加內(nèi)容,或引入其他元素,或甚至移除該標(biāo)簽。因此,這時瀏覽器會停止處理頁面,先執(zhí)行 JavaScript代碼,然后再繼續(xù)解析和渲染頁面。同樣的情況也發(fā)生在使用 src 屬性加載 JavaScript的過程中,瀏覽器必須先花時間下載外鏈文件中的代碼,然后解析并執(zhí)行它。在這個過程中,頁面渲染和用戶交互完全被阻塞了。
HTML 4 規(guī)范指出 <script> 標(biāo)簽可以放在 HTML 文檔的<head>或<body>中,并允許出現(xiàn)多次。Web 開發(fā)人員一般習(xí)慣在 <head> 中加載外鏈的 JavaScript,接著用 <link> 標(biāo)簽用來加載外鏈的 CSS 文件或者其他頁面信息。例如清單 2。
清單 2 低效率腳本位置示例
然而這種常規(guī)的做法卻隱藏著嚴(yán)重的性能問題。在清單 2 的示例中,當(dāng)瀏覽器解析到 <script> 標(biāo)簽(第 4 行)時,瀏覽器會停止解析其后的內(nèi)容,而優(yōu)先下載腳本文件,并執(zhí)行其中的代碼,這意味著,其后的 styles.css 樣式文件和<body>標(biāo)簽都無法被加載,由于<body>標(biāo)簽無法被加載,那么頁面自然就無法渲染了。因此在該 JavaScript 代碼完全執(zhí)行完之前,頁面都是一片空白。圖 1 描述了頁面加載過程中腳本和樣式文件的下載過程。
圖 1 JavaScript 文件的加載和執(zhí)行阻塞其他文件的下載
我們可以發(fā)現(xiàn)一個有趣的現(xiàn)象:第一個 JavaScript 文件開始下載,與此同時阻塞了頁面其他文件的下載。此外,從 script1.js 下載完成到 script2.js 開始下載前存在一個延時,這段時間正好是 script1.js 文件的執(zhí)行過程。每個文件必須等到前一個文件下載并執(zhí)行完成才會開始下載。在這些文件逐個下載過程中,用戶看到的是一片空白的頁面。
從 IE 8、Firefox 3.5、Safari 4 和 Chrome 2 開始都允許并行下載 JavaScript 文件。這是個好消息,因為<script>標(biāo)簽在下載外部資源時不會阻塞其他<script>標(biāo)簽。遺憾的是,JavaScript 下載過程仍然會阻塞其他資源的下載,比如樣式文件和圖片。盡管腳本的下載過程不會互相影響,但頁面仍然必須等待所有 JavaScript 代碼下載并執(zhí)行完成才能繼續(xù)。因此,盡管最新的瀏覽器通過允許并行下載提高了性能,但問題尚未完全解決,腳本阻塞仍然是一個問題。
由于腳本會阻塞頁面其他資源的下載,因此推薦將所有<script>標(biāo)簽盡可能放到<body>標(biāo)簽的底部,以盡量減少對整個頁面下載的影響。例如清單 3
清單 3 推薦的代碼放置位置示例
這段代碼展示了在 HTML 文檔中放置<script>標(biāo)簽的推薦位置。盡管腳本下載會阻塞另一個腳本,但是頁面的大部分內(nèi)容都已經(jīng)下載完成并顯示給了用戶,因此頁面下載不會顯得太慢。
這是優(yōu)化 JavaScript 的首要規(guī)則:將腳本放在底部。
由于每個<script>標(biāo)簽初始下載時都會阻塞頁面渲染,所以減少頁面包含的<script>標(biāo)簽數(shù)量有助于改善這一情況。這不僅針對外鏈腳本,內(nèi)嵌腳本的數(shù)量同樣也要限制。瀏覽器在解析 HTML 頁面的過程中每遇到一個<script>標(biāo)簽,都會因執(zhí)行腳本而導(dǎo)致一定的延時,因此最小化延遲時間將會明顯改善頁面的總體性能。
這個問題在處理外鏈 JavaScript 文件時略有不同。考慮到 HTTP 請求會帶來額外的性能開銷,因此下載單個 100Kb 的文件將比下載 5 個 20Kb 的文件更快。也就是說,減少頁面中外鏈腳本的數(shù)量將會改善性能。
通常一個大型網(wǎng)站或應(yīng)用需要依賴數(shù)個 JavaScript 文件。您可以把多個文件合并成一個,這樣只需要引用一個<script>標(biāo)簽,就可以減少性能消耗。文件合并的工作可通過離線的打包工具或者一些實時的在線服務(wù)來實現(xiàn)。
需要特別提醒的是,把一段內(nèi)嵌腳本放在引用外鏈樣式表的<link>之后會導(dǎo)致頁面阻塞去等待樣式表的下載。這樣做是為了確保內(nèi)嵌腳本在執(zhí)行時能獲得最精確的樣式信息。因此,建議不要把內(nèi)嵌腳本緊跟在<link>標(biāo)簽后面。
減少 JavaScript 文件大小并限制 HTTP 請求數(shù)在功能豐富的 Web 應(yīng)用或大型網(wǎng)站上并不總是可行。Web 應(yīng)用的功能越豐富,所需要的 JavaScript 代碼就越多,盡管下載單個較大的 JavaScript 文件只產(chǎn)生一次 HTTP 請求,卻會鎖死瀏覽器的一大段時間。為避免這種情況,需要通過一些特定的技術(shù)向頁面中逐步加載 JavaScript 文件,這樣做在某種程度上來說不會阻塞瀏覽器。
無阻塞腳本的秘訣在于,在頁面加載完成后才加載 JavaScript 代碼。這就意味著在 window 對象的 onload事件觸發(fā)后再下載腳本。有多種方式可以實現(xiàn)這一效果。
HTML 4 為<script>標(biāo)簽定義了一個擴展屬性:defer。Defer 屬性指明本元素所含的腳本不會修改 DOM,因此代碼能安全地延遲執(zhí)行。defer 屬性只被 IE 4 和 Firefox 3.5 更高版本的瀏覽器所支持,所以它不是一個理想的跨瀏覽器解決方案。在其他瀏覽器中,defer 屬性會被直接忽略,因此<script>標(biāo)簽會以默認(rèn)的方式處理,也就是說會造成阻塞。然而,如果您的目標(biāo)瀏覽器支持的話,這仍然是個有用的解決方案。清單 4 是一個例子
清單 4 defer 屬性使用方法示例
<script type="text/javascript" src="script1.js" defer></script>
帶有 defer 屬性的<script>標(biāo)簽可以放置在文檔的任何位置。對應(yīng)的 JavaScript 文件將在頁面解析到<script>標(biāo)簽時開始下載,但不會執(zhí)行,直到 DOM 加載完成,即onload事件觸發(fā)前才會被執(zhí)行。當(dāng)一個帶有 defer 屬性的 JavaScript 文件下載時,它不會阻塞瀏覽器的其他進程,因此這類文件可以與其他資源文件一起并行下載。
任何帶有 defer 屬性的<script>元素在 DOM 完成加載之前都不會被執(zhí)行,無論內(nèi)嵌或者是外鏈腳本都是如此。清單 5 的例子展示了defer屬性如何影響腳本行為:
清單 5 defer 屬性對腳本行為的影響
這段代碼在頁面處理過程中彈出三次對話框。不支持 defer 屬性的瀏覽器的彈出順序是:"defer"、"script"、"load"。而在支持 defer 屬性的瀏覽器上,彈出的順序則是:"script"、"defer"、"load"。請注意,帶有 defer 屬性的<script>元素不是跟在第二個后面執(zhí)行,而是在 onload 事件被觸發(fā)前被調(diào)用。
如果您的目標(biāo)瀏覽器只包括 Internet Explorer 和 Firefox 3.5,那么 defer 腳本確實有用。如果您需要支持跨領(lǐng)域的多種瀏覽器,那么還有更一致的實現(xiàn)方式。
HTML 5 為<script>標(biāo)簽定義了一個新的擴展屬性:async。它的作用和 defer 一樣,能夠異步地加載和執(zhí)行腳本,不因為加載腳本而阻塞頁面的加載。但是有一點需要注意,在有 async 的情況下,JavaScript 腳本一旦下載好了就會執(zhí)行,所以很有可能不是按照原本的順序來執(zhí)行的。如果 JavaScript 腳本前后有依賴性,使用 async 就很有可能出現(xiàn)錯誤。
文檔對象模型(DOM)允許您使用 JavaScript 動態(tài)創(chuàng)建 HTML 的幾乎全部文檔內(nèi)容。<script>元素與頁面其他元素一樣,可以非常容易地通過標(biāo)準(zhǔn) DOM 函數(shù)創(chuàng)建:
清單 6 通過標(biāo)準(zhǔn) DOM 函數(shù)創(chuàng)建<script>元素
新的<script>元素加載 script1.js 源文件。此文件當(dāng)元素添加到頁面之后立刻開始下載。此技術(shù)的重點在于:無論在何處啟動下載,文件的下載和運行都不會阻塞其他頁面處理過程。您甚至可以將這些代碼放在<head>部分而不會對其余部分的頁面代碼造成影響(除了用于下載文件的 HTTP 連接)。
當(dāng)文件使用動態(tài)腳本節(jié)點下載時,返回的代碼通常立即執(zhí)行(除了 Firefox 和 Opera,他們將等待此前的所有動態(tài)腳本節(jié)點執(zhí)行完畢)。當(dāng)腳本是"自運行"類型時,這一機制運行正常,但是如果腳本只包含供頁面其他腳本調(diào)用調(diào)用的接口,則會帶來問題。這種情況下,您需要跟蹤腳本下載完成并是否準(zhǔn)備妥善。可以使用動態(tài) <script> 節(jié)點發(fā)出事件得到相關(guān)信息。
Firefox、Opera, Chorme 和 Safari 3+會在<script>節(jié)點接收完成之后發(fā)出一個 onload 事件。您可以監(jiān)聽這一事件,以得到腳本準(zhǔn)備好的通知:
清單 7 通過監(jiān)聽 onload 事件加載 JavaScript 腳本
Internet Explorer 支持另一種實現(xiàn)方式,它發(fā)出一個 readystatechange 事件。<script>元素有一個 readyState 屬性,它的值隨著下載外部文件的過程而改變。readyState 有五種取值:
· "uninitialized":默認(rèn)狀態(tài)
· "loading":下載開始
· "loaded":下載完成
· "interactive":下載完成但尚不可用
· "complete":所有數(shù)據(jù)已經(jīng)準(zhǔn)備好
微軟文檔上說,在<script>元素的生命周期中,readyState 的這些取值不一定全部出現(xiàn),但并沒有指出哪些取值總會被用到。實踐中,我們最感興趣的是"loaded"和"complete"狀態(tài)。Internet Explorer 對這兩個 readyState 值所表示的最終狀態(tài)并不一致,有時<script>元素會得到"loader"卻從不出現(xiàn)"complete",但另外一些情況下出現(xiàn)"complete"而用不到"loaded"。最安全的辦法就是在 readystatechange 事件中檢查這兩種狀態(tài),并且當(dāng)其中一種狀態(tài)出現(xiàn)時,刪除 readystatechange 事件句柄(保證事件不會被處理兩次):
清單 8 通過檢查 readyState 狀態(tài)加載 JavaScript 腳本
大多數(shù)情況下,您希望調(diào)用一個函數(shù)就可以實現(xiàn) JavaScript 文件的動態(tài)加載。下面的函數(shù)封裝了標(biāo)準(zhǔn)實現(xiàn)和 IE 實現(xiàn)所需的功能:
清單 9 通過函數(shù)進行封裝
此函數(shù)接收兩個參數(shù):JavaScript 文件的 URL,和一個當(dāng) JavaScript 接收完成時觸發(fā)的回調(diào)函數(shù)。屬性檢查用于決定監(jiān)視哪種事件。最后一步,設(shè)置 src 屬性,并將<script>元素添加至頁面。此 loadScript() 函數(shù)使用方法如下:
清單 10 loadScript()函數(shù)使用方法
您可以在頁面中動態(tài)加載很多 JavaScript 文件,但要注意,瀏覽器不保證文件加載的順序。所有主流瀏覽器之中,只有 Firefox 和 Opera 保證腳本按照您指定的順序執(zhí)行。其他瀏覽器將按照服務(wù)器返回它們的次序下載并運行不同的代碼文件。您可以將下載操作串聯(lián)在一起以保證他們的次序,如下:
清單 11 通過 loadScript()函數(shù)加載多個 JavaScript 腳本
此代碼等待 script1.js 可用之后才開始加載 script2.js,等 script2.js 可用之后才開始加載 script3.js。雖然此方法可行,但如果要下載和執(zhí)行的文件很多,還是有些麻煩。如果多個文件的次序十分重要,更好的辦法是將這些文件按照正確的次序連接成一個文件。獨立文件可以一次性下載所有代碼(由于這是異步進行的,使用一個大文件并沒有什么損失)。
動態(tài)腳本加載是非阻塞 JavaScript 下載中最常用的模式,因為它可以跨瀏覽器,而且簡單易用。
此技術(shù)首先創(chuàng)建一個 XHR 對象,然后下載 JavaScript 文件,接著用一個動態(tài) <script> 元素將 JavaScript 代碼注入頁面。清單 12 是一個簡單的例子:
清單 12 通過 XHR 對象加載 JavaScript 腳本
此代碼向服務(wù)器發(fā)送一個獲取 script1.js 文件的 GET 請求。onreadystatechange 事件處理函數(shù)檢查 readyState 是不是 4,然后檢查 HTTP 狀態(tài)碼是不是有效(2XX 表示有效的回應(yīng),304 表示一個緩存響應(yīng))。如果收到了一個有效的響應(yīng),那么就創(chuàng)建一個新的<script>元素,將它的文本屬性設(shè)置為從服務(wù)器接收到的 responseText 字符串。這樣做實際上會創(chuàng)建一個帶有內(nèi)聯(lián)代碼的<script>元素。一旦新<script>元素被添加到文檔,代碼將被執(zhí)行,并準(zhǔn)備使用。
這種方法的主要優(yōu)點是,您可以下載不立即執(zhí)行的 JavaScript 代碼。由于代碼返回在<script>標(biāo)簽之外(換句話說不受<script>標(biāo)簽約束),它下載后不會自動執(zhí)行,這使得您可以推遲執(zhí)行,直到一切都準(zhǔn)備好了。另一個優(yōu)點是,同樣的代碼在所有現(xiàn)代瀏覽器中都不會引發(fā)異常。
此方法最主要的限制是:JavaScript 文件必須與頁面放置在同一個域內(nèi),不能從 CDN 下載(CDN 指"內(nèi)容投遞網(wǎng)絡(luò)(Content Delivery Network)",所以大型網(wǎng)頁通常不采用 XHR 腳本注入技術(shù)。
一般而言,減少 JavaScript 對性能的影響有以下幾種方法:
· A.將所有的<script>標(biāo)簽放到頁面底部,也就是</body>閉合標(biāo)簽之前,這能確保在腳本執(zhí)行前頁面已經(jīng)完成了渲染。
· B.盡可能地合并腳本。頁面中的<script>標(biāo)簽越少,加載也就越快,響應(yīng)也越迅速。無論是外鏈腳本還是內(nèi)嵌腳本都是如此。
· C.采用無阻塞下載 JavaScript 腳本的方法:
· a-使用<script>標(biāo)簽的 defer 屬性(僅適用于 IE 和 Firefox 3.5 以上版本);
· b-使用動態(tài)創(chuàng)建的<script>元素來下載并執(zhí)行代碼;
· c-使用 XHR 對象下載 JavaScript 代碼并注入頁面中。
通過以上策略,可以在很大程度上提高那些需要使用大量 JavaScript 的 Web 網(wǎng)站和應(yīng)用的實際性能。
看完了,別忘了收藏、轉(zhuǎn)發(fā)、點個贊,更別忘了要關(guān)注本號!^_^
SS加載確實有可能阻塞頁面加載,但這并非絕對,具體取決于CSS的加載方式、應(yīng)用位置以及瀏覽器的渲染機制。在了解CSS加載如何影響頁面加載之前,我們先要明白瀏覽器渲染頁面的基本流程。
瀏覽器在加載網(wǎng)頁時,會按照從上到下的順序解析HTML文檔。當(dāng)瀏覽器遇到`<link>`標(biāo)簽引用外部CSS文件時,它會停止HTML的解析,轉(zhuǎn)而加載并應(yīng)用這個CSS文件。這個過程被稱為CSS阻塞。因此,如果這個CSS文件很大或者加載速度很慢,用戶可能會看到一個空白頁面,直到CSS文件完全加載并應(yīng)用。
然而,有幾種方法可以避免或減輕CSS加載對頁面加載的阻塞:
此外,值得注意的是,現(xiàn)代瀏覽器通常具有一些優(yōu)化機制,如并行下載、緩存等,這些都可以幫助減少CSS加載對頁面加載的影響。
總的來說,CSS加載確實有可能阻塞頁面加載,但通過一些優(yōu)化策略和技術(shù),我們可以減輕或避免這種阻塞。選擇哪種策略取決于你的具體需求和約束。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。