# Infinite Scroll 實作
###### tags: `實作` `AJAX` `javascript`
[實作頁面連結](https://allenw0815.github.io/mission/mission_25/index.html)

### 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)