# Infinite Scroll 實作 ###### tags: `實作` `AJAX` `javascript` [實作頁面連結](https://allenw0815.github.io/mission/mission_25/index.html) ![](https://i.imgur.com/9yNxdrU.png) ### Flowchart 1. 寫 HTML 結構,只需要輸入框及結果區即可 2. CSS 沒特別的 3. JS AJAX 拿資料後顯示,會用到 scroll 事件 > JS 流程 1.取好 DOM 拿資料,並用變數讓資料 api 可變,屬非同步動作需要 Promise 物件處理。 ```javascript= let limit = 5 // 資料數 let page = 1 // 頁數 async function getPosts() { const res = await fetch(`https://jsonplaceholder.typicode.com/posts?_limit=${limit}&_page=${page}`) return await res.json() } ``` 2.產生內容,這邊用了兩種寫法,分別為無數字及有數字 1 的 function 1. 每一次都 call API 拿全部資料,依照筆數產生 2. 每一次拿一定的筆數陸續產生 ```javascript= // way01 async function showPosts(){ const posts = await getPosts() let display = '' for (let i = 0 ; i < posts.length ; i++ ){ display +=` <div class="post"> <div class="number">${posts[i].id}</div> <div class="post-info"> <h2 class="title">${posts[i].title}</h2> <p class="body">${posts[i].body}</p> </div> </div>` } resultContainer.innerHTML = display // 跑全部 } function showLoader() { loader.classList.add('show') setTimeout(() => { loader.classList.remove('show') setTimeout(() => { limit += 5 ; //每次都多拿5筆重新 render showPosts() } , 300) } , 1200) } // way02 async function showPosts2(){ const posts = await getPosts() posts.forEach( post =>{ const postEl = document.createElement('div') postEl.classList.add('post') postEl.innerHTML =` <div class="number">${post.id}</div> <div class="post-info"> <h2 class="title">${post.title}</h2> <p class="body">${post.body}</p> </div>` resultContainer.appendChild(postEl) // 陸續添加 }) } function showLoader2() { loader.classList.add('show') setTimeout(() => { loader.classList.remove('show') setTimeout(() => { page ++ ; //每次拿後5筆往下 render showPosts2() } , 200) } , 500) } ``` 3.捲動到底時觸發事件,添加 loading 動畫定時移除( 透過 CSS opacity ),並且在定時後更改 API 的 URL 再重新調用產生內容的 function ( 見上方步驟2 ) [各種使用者尺寸](https://shubo.io/element-size-scrolling/) ```javascript= window.addEventListener('scroll',()=>{ const {scrollTop,scrollHeight,clientHeight} = document.documentElement if(scrollTop+clientHeight >= scrollHeight - 3){showLoader2()} // 超出卷軸的距離 + 當前視窗高度 >= 整個完整高度 // 當完全相等的時候代表卷軸尬底 // -3 讓使用者幾乎到底時就可以觸發 不用完全到底 增加體驗 ``` 4.搜尋功能,透過文字檢查是否包含後添加 CSS 隱藏實現,這邊都轉為小寫增加準確度 ```javascript= filter.addEventListener('input',(e)=>{ const compareTarget = e.target.value.toLowerCase() const posts = document.querySelectorAll('.post') posts.forEach(post=>{ const title = post.querySelector('.title').innerText.toLowerCase() const body = post.querySelector('.body').innerText.toLowerCase() title.includes(compareTarget) || body.includes(compareTarget) ? post.style.display = 'flex' : post.style.display = 'none' }) }) ``` ### 紀錄 * 這邊的搜尋是比對標題及內文,然後透過 CSS 的display 來顯示結果 ```javascript= title.includes(compareTarget) ? post.style.display = 'flex' : post.style.display = 'none' ``` * 比對文字功能也可以用 indexOf() 回傳的索引值正負數( 若無回傳 -1 )判斷 ```javascript= let array = [2, 9, 12]; array.indexOf(2); // 0 array.indexOf(7); // -1 ``` * 這個 includes() 對於陣列跟字串都能用,一魚兩吃,讚 ```javascript= 'Blue Whale'.includes('blue') // returns false [1, 2, 3].includes(2); // true [1, 2, 3].includes(4); // false ``` ### 小記 不會太難,但有在熟悉到一些非同步的練習 可以作一些補充 * 做一鍵返回頁面最上層 (done) ### 參考 [Array.prototype.indexOf()](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) [String.prototype.includes()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) [Array.prototype.includes()](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)