Skip to content

Commit

Permalink
Ch.13 將計算機改為 Observer
Browse files Browse the repository at this point in the history
  • Loading branch information
rogeraabbccdd committed Sep 22, 2023
1 parent c96cd4f commit f92b9aa
Showing 1 changed file with 280 additions and 73 deletions.
353 changes: 280 additions & 73 deletions docs/views/ch13.md
Original file line number Diff line number Diff line change
@@ -1,94 +1,301 @@
---
title: Ch.13 簡易計算機
title: Ch.13 Observer
tags:
- JavaScript
prev: ./ch12
next: ./ch14
---
製作簡易計算機,練習事件
Observer 監視器,可以觀察 DOM 元素的變化,當元素變化時執行指定的 function
<!-- more -->
## 計算機
:::danger
`eval()` 是非常危險的語法,使用時須特別注意
## Mutation Observer
當觀察的 DOM 元素變動時會觸發
::: demo [vanilla]
```html
<html>
<input type="button" id="btn-mutation" value="點按鈕修改 div">
<div id="div-mutation"></div>
</html>
<style>
#div-mutation{
width: 100px;
height: 100px;
background: black;
color: white;
}
</style>
<script>
// 取得要觀察的元素
const div = document.getElementById('div-mutation')
// 建立一個觀察者
// new MutationObserver(觀察到變更時執行的 function)
const observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
// type - 變動類型
// target - 變動的元素
// addedNodes - 被新增的節點
// removedNodes - 被移除的節點
// attributeName - 變動的屬性
// oldValue - 變動前的值
console.log(mutation)
}
})
// 設定觀察元素
observer.observe(div, {
// 是否觀察下一層元素
childList: true,
// 是否觀察所有內層
subtree: true,
// 是否觀察屬性變動
attributes: true,
// 是否觀察內容變動
characterData: true,
// 是否紀錄舊屬性
attributeOldValue: true,
// 是否紀錄舊內容
characterDataOldValue: true,
// 指定觀察的屬性名稱,沒設定就是全部
// attributeFilter: ['href']
})
// 停止觀察
// observer.disconnect()
const btn = document.getElementById('btn-mutation')
btn.addEventListener('click', () => {
// 修改元素,檢查是否觸發觀察者
div.innerText += 'a'
})
</script>
```
:::

## Resize Observer
當觀察的 DOM 元素縮放時會觸發
::: demo [vanilla]
```html
<html>
<input type="button" id="btn-resize" value="點按鈕修改 div 邊框">
<div class="div-resize" id="div-resize1"></div>
<div class="div-resize" id="div-resize2"></div>
</html>
<style>
.div-resize{
width: 100px;
height: 100px;
background: gray;
margin: 10px;
}
</style>
<script>
// 取得要觀察的元素
const div1 = document.getElementById('div-resize1')
const div2 = document.getElementById('div-resize2')
// 建立一個觀察者
// new ResizeObserver(觀察到變更時執行的 function)
const observer = new ResizeObserver(mutations => {
for (const mutation of mutations) {
// target - 變動的元素
// contentRect - 元素的寬高與座標
// borderBoxSize.blockSize - 元素的高度
// borderBoxSize.inlineSize - 元素的寬度
// contentBoxSize.blockSize - 元素的高度
// contentBoxSize.inlineSize - 元素的寬度
console.log(mutation)
}
})
// 設定觀察元素
observer.observe(div1, {
// 設定觀察元素的寬高計算方式
// content-box: 元素的寬高不包含邊框
// border-box: 元素的寬高包含邊框
box: 'border-box'
})
observer.observe(div2, {
box: 'content-box'
})
// 停止觀察
// observer.disconnect()
// 停止觀察某元素
// observer.unobserve(div1)
// 修改元素,檢查是否觸發觀察者
// 如果有一連串尺寸變動,會合併成一次紀錄,且只記錄最終結果
const btn = document.getElementById('btn-resize')
btn.addEventListener('click', () => {
// 修改元素,檢查是否觸發觀察者
div1.style.border = '10px solid black'
div1.style.border = '10px solid black'
div2.style.border = '10px solid black'
div2.style.border = '10px solid black'
})
</script>
```
:::

## Intersection Observer
當元素相交時會觸發
::: demo [vanilla]
```html
<html>
<div id="content-out">
<div id="content-in">
<table>
<tr>
<td colspan="4" id="input">0</td>
</tr>
<tr>
<td>C</td>
<td>/</td>
<td>*</td>
<td>-</td>
</tr>
<tr>
<td>7</td>
<td>8</td>
<td>9</td>
<td rowspan="2">+</td>
</tr>
<tr>
<td>4</td>
<td>5</td>
<td>6</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td rowspan="2">=</td>
</tr>
<tr>
<td colspan="2">0</td>
<td>.</td>
</tr>
</table>
</div>
</div>
<p id="intersection-info">isIntersecting = false</p>
<div id="intersection-container">
<div class="intersection-pad"></div>
<div id="intersection-target"></div>
<div class="intersection-pad"></div>
</div>
</html>
<style>
#intersection-container {
height: 300px;
width: 100%;
position: relative;
overflow-y: scroll;
background: white;
}
.intersection-pad {
height: 1500px;
width: 100%;
}
#intersection-target {
background: rgb(237, 28, 36);
height: 100px;
outline: 50px solid rgba(0, 0, 0, 0.2);
}
</style>
<script>
const input = document.getElementById("input")
const td = document.querySelectorAll("td:not(#input)")
for (let i = 0; i < td.length; i++) {
td[i].onclick = function () {
console.log(this.innerText)
if (this.innerText == "=") {
input.innerText = eval(input.innerText)
} else if (this.innerText == "C") {
input.innerText = "0"
} else {
if(input.innerText == "0"){
input.innerText = this.innerText
}
else{
input.insertAdjacentHTML("beforeend", this.innerText)
}
}
}
}
document.onkeydown = (event) => {
if (event.key === 'Enter') input.innerText = eval(input.innerText)
else if (!isNaN(parseInt(event.key)) || event.key === '+' || event.key === '-' || event.key === '*' || event.key === '/' || event.key === '.') {
if (input.innerText == "0") {
input.innerText = event.key
} else {
input.insertAdjacentHTML("beforeend", event.key)
}
}
}
// 取得要觀察的元素
const div = document.getElementById('intersection-target')
const info = document.getElementById('intersection-info')
// 建立一個觀察者
// new IntersectionObserver(觀察到變更時執行的 function, 設定)
// entries - 相交的元素
// owner - IntersectionObserver 設定
const observer = new IntersectionObserver((entries, owner) => {
console.log(owner)
for (const entry of entries) {
// target - 變動的元素
// isIntersecting - 是否與可視範圍相交
// intersectionRatio - 相交比例,相交面積 / 目標元素面積
// boundingClientRect - 目標元素的尺寸與座標
// rootBounds - root 的尺寸與座標
// intersectionRect - 相交的範圍
// time - 相交的時間,從 IntersectionObserver 被建立的時間開始計算,單位為毫秒
console.log(entry)
info.innerText = 'isIntersecting = ' + entry.isIntersecting
}
}, {
// 以哪個元素為依據,預設為 null,表示以瀏覽器的可視窗口作為依據
root: null,
// 計算相交時的偏移量
rootMargin: "50px",
// 設定觸發的比例門檻,若設定為 0.5,則元素 50% 出現和離開就會觸發,也可以設定為陣列
threshold: 0.5,
})
// 停止觀察
// observer.disconnect()
// 停止觀察某元素
// observer.unobserve(div1)
observer.observe(div)
</script>
```
:::

## 應用
圖片 Lazy load
::: demo [vanilla]
```html
<html>
<div id="lazyload-container">
<div class="lazyload-pad"></div>
<img id="lazyload-target" src="">
<div class="lazyload-pad"></div>
</div>
</html>
<style>
#lazyload-container {
height: 300px;
width: 100%;
position: relative;
overflow-y: scroll;
background: white;
text-align: center;
}
.lazyload-pad {
height: 1500px;
width: 100%;
}
#lazyload-target {
width: 200px;
height: 200px;
border: 1px solid black;
}
</style>
<script>
const imgTarget = document.getElementById('lazyload-target')
const observer = new IntersectionObserver((entries, owner) => {
for (const entry of entries) {
if (entry.isIntersecting) {
imgTarget.src = 'https://picsum.photos/200/200'
} else {
imgTarget.src = ''
}
}
}, {
root: null,
rootMargin: "50px 0px 50px 0px",
threshold: 0,
})
observer.observe(imgTarget)
</script>
```
:::

:::warning 作業
美化你的計算機,或加入更多的功能
無限滾動 Infinite scroll
::: demo [vanilla]
```html
<html>
<div id="infinite-container">
<ul>
<li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste sequi voluptatem voluptate magni, sunt itaque assumenda ipsa odit porro, nobis ratione, quibusdam repellendus molestiae odio ipsam eum suscipit quos provident!</li>
<li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste sequi voluptatem voluptate magni, sunt itaque assumenda ipsa odit porro, nobis ratione, quibusdam repellendus molestiae odio ipsam eum suscipit quos provident!</li>
<li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste sequi voluptatem voluptate magni, sunt itaque assumenda ipsa odit porro, nobis ratione, quibusdam repellendus molestiae odio ipsam eum suscipit quos provident!</li>
<li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste sequi voluptatem voluptate magni, sunt itaque assumenda ipsa odit porro, nobis ratione, quibusdam repellendus molestiae odio ipsam eum suscipit quos provident!</li>
<li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste sequi voluptatem voluptate magni, sunt itaque assumenda ipsa odit porro, nobis ratione, quibusdam repellendus molestiae odio ipsam eum suscipit quos provident!</li>
<li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste sequi voluptatem voluptate magni, sunt itaque assumenda ipsa odit porro, nobis ratione, quibusdam repellendus molestiae odio ipsam eum suscipit quos provident!</li>
<li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste sequi voluptatem voluptate magni, sunt itaque assumenda ipsa odit porro, nobis ratione, quibusdam repellendus molestiae odio ipsam eum suscipit quos provident!</li>
</ul>
</div>
</html>
<style>
#infinite-container {
height: 300px;
width: 100%;
position: relative;
overflow-y: scroll;
background: white;
background: gray;
color: black;
}
</style>
<script>
const observer = new IntersectionObserver((entries, owner) => {
if (entries[0].isIntersecting) {
const html = '<li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste sequi voluptatem voluptate magni, sunt itaque assumenda ipsa odit porro, nobis ratione, quibusdam repellendus molestiae odio ipsam eum suscipit quos provident!</li>'
document.querySelector("#infinite-container ul").innerHTML += Array(10).fill(html).join('');
observer.unobserve(entries[0].target);
observer.observe(document.querySelector("#infinite-container li:nth-last-child(2)"));
}
})
observer.observe(document.querySelector('#infinite-container li:nth-last-child(2)'))
</script>
```
:::

0 comments on commit f92b9aa

Please sign in to comment.