本篇會介紹怎麼用 JavaScript 操控 DOM 元素,如何監聽不同的瀏覽器事件並做出反應
DOM (Document Oject Model)
什麼是 DOM? 簡單理解就是把 Document 的元素變成物件
舉例來說,我們可以選取該網頁文件,找到裡面的 .header,新增文字,賦予值叫 Hello World:
document.querySelector(".header").textContent = "Hello World"
選取 DOM 元素
getElement
getElementByTagName('div')
選到標籤
getElementByClassName('block')
選到該 class
getElementByID('header')
選到某 id
querySelector
可以直接用 CSS 選取器的方法來選去 DOM 元素
querySelector('div')
querySelector('.className')
querySelector('#id')
但要注意 querySelector
只會選到第一個,如果要選多個,需要使用 querySelectorAll
querySelectorAll('div')
// 選到全部的 div
把 DOM 元素 存為一個變數方便每次使用
let element = document.querySelector(".header")
NODE 其他選取方式
parentNode
選到父節點
parentElement
選到父元素
childNodes
選到子節點 // 要注意它也會把空格也視為節點
children
選到子元素 // 只會選到 element
firstChild
選到第一個子節點 // 要注意也會選到空格節點
firstElementChild
選到第一個子元素
lastChild
選到最後一個子節點 // 要注意也會選到空格節點
lastElementChild
選到最後一個子元素
nextSibling
選到下一個節點 // 會選到空格節點
nextElementSibling
選到下一個元素
previousSibling
選到上一個節點 // 會選到空格節點
previousElementSibling
選到上一個元素
改變 DOM 元素
直接幫 DOM 元素改 CSS 樣式
.style[padding-top"] = "10px"
.style["paddingTop"] = "10px"
改變 DOM 元素的 class
.classList.add("active")
// 增加 active 這個 class
.classList.remove("active")
// 移除 active 這個 class
.classList.toggle("active")
// 開關 active 這個 class
.classList.contains("sample")
// 有沒有這個 class
<body>
<div id="block">Hello World!</div>
</body>
<style>
.class {
font-size: 14px;
}
</style>
<script>
const element = document.querySelector("#block");
element.classList.add("active")
</script>
改變 DOM 元素的內容
.innertext = "hello world"
// 抓 DOM 元素裡文字內容來改
.innerHTML = "hello world"
// 把 DOM 元素中全部的東西拿出來
.outterHTML = "hello world"
// 把 DOM 元素(含 DOM 元素本身)全部的東西抓出來
.textContent = "Hello World"
// 賦值
.setAttribute("style", "color: red")
// 賦屬性
.innerText
關注 style,而 textContnet
則不
增加屬性
.setAttribute('title', 'Hello Div')
插入與刪除元素
createElement
來建立一個物件
createTextNode
建立純文字
.insertBefore(newDiv, h1)
在 h1 之前插入 newDiv
.appendChild
把物件插入回 HTML 內
<body>
<div id="block">Hello World!</div>
</body>
<script>
const element = document.querySelector("#block");
const item = document.createElement("div") //插入 <div>
const item = document.createElement("123") //插入 123
element.appendChild(item)
</script>
用 removeChild
把物件刪除
element.removeChild(document.querySelector("a"))
// 把 a 元素刪除
Event
e.clientX
/ e.clientY
is from the browser 找絕對座標
e.offsetX
/ e.offsetY
is from the target itse 找相對座標
e.altKey
/ e.ctrlKey
/ e.shiftKey
檢查這些按鍵是否按著
mouseenter
/ mouseover
滑鼠移進移出
mouseleave
/ mouseout
滑鼠移進移出
上面兩組事件看起來功能一樣,但在冒泡事件上的支援不同,mouseenter 和 mouseleave 不支援冒泡事件,舉例而言,在一個元素上的子元素上進出或離開的時候,會觸發 mouseover 和 mouseout 事件,但卻不會觸發 mouseenter 和 mouseleave 事件
體驗看看
mousemove
滑鼠移動
keydown
/ keyup
/ keypress
鍵盤輸入
focus
對焦 input 框 / blur
取消對焦
cut
/ paste
input
所有對 input 可以做的事都會觸發這個事件
Event Listenser
element.addEventListener("監聽的事件名稱", 要做什麼的函式)
<body>
<div id="block">Hello World!</div>
</body>
<script>
const element = document.querySelector("#block");
element.addEventListener("click", function(){
alert("click!") // 匿名函式
})
</script>
e 是什麼?
瀏覽器會呼叫這個 function 的時候帶的參數,利用這個參數來拿到這個事件相關的資訊
const element = document.querySelector("#block");
element.addEventListener("click", function(e){
console.log(e) //該事件所有的參數
console.log(e.target) // 該事件觸發的目標
}
表單處理
<script>
const element = document.querySelector(".login-form");
element.addEventListener("submit", function(e){
alert("submit");
})
</script>
用 e.preventDefault()
可以阻止預設行為
<script>
const element = document.querySelector(".login-form");
element.addEventListener("submit", function(e){
e.preventDefault()
})
</script>
事件傳遞機制
事件傳遞可以分為三個階段,事件捕獲 -> 目標 -> 事件冒泡,可以利用 addEventListener 的第三個參數來決定要再捕獲或冒泡階段去監聽這個事件,但當事件傳到 target 本身,沒有分捕獲跟冒泡
true 代表添加到捕獲階段;false (預設) 代表添加到冒泡階段
事件傳遞會從 DOM tree 的最外層開始向內層傳遞,直到觸發事件的元素(e.target),再往上層冒泡至 DOM tree 的最外層。當事件傳遞到觸發事件的元素(e.target)時,是沒有分冒泡或捕獲階段的,無論 addEventListener
的第三個參數是 true 還是 false,e.eventPhase
都是 AT_TARGET。
注意:當事件傳遞到點擊的真正對象,也就是 e.target 時,無論使用 addEventListener 的第三個參數是 true 還是 false,這邊的 e.eventPhase 都會變成 AT_TARGET。
preventDefault
跟 JavaScript 的事件傳遞「一點關係都沒有」,加上這一行後,事件還是會繼續往下傳遞。
捕獲:事件傳遞機制中,由 DOM tree 最外層向內逐層傳遞,觸發各節點的事件監聽。
冒泡:事件傳遞機制中由觸發事件的元素,向外逐層傳遞觸發各元素的事件監聽,直到 DOM tree 的最外層。
停止事件傳遞
停止傳遞
e.stopPropagation()
要注意同層級剩下的 listener 仍然會被執行
停止所有同層級的事件傳遞
e.stopImmediatePropagation()
阻擋預設行為
event.preventDefault()
:是阻擋事件的預設行為,和事件傳遞沒有關係,並不影響事件傳遞。像點擊 <a>
連結會觸發連結到某個網址,使用 event.preventDefault()
可以阻擋這個動作。
事件代理
利用事件傳遞機制的原理,去監聽外層的元素,判斷 e.target
是我們要的目標節點時,再去做後續的操作,在很多情況下就可以不用去重複監聽無數個目標物,底下有個簡單的範例:
有一個 ul,底下 1000 個 li,如果你幫每一個 li 都加上一個 eventListener,你就新建了 1000 個 function。
但我們剛剛已經知道,任何點擊 li 的事件其實都會傳到 ul 身上,因此我們可以在 ul 身上掛一個 listener 就好。
<!DOCTYPE html>
<html>
<body>
<ul id="list">
<li data-index="1">1</li>
<li data-index="2">2</li>
<li data-index="3">3</li>
</ul>
</body>
</html>
document.getElementById('list').addEventListener('click', (e) => {
console.log(e.target.getAttribute('data-index'));
})
而這樣的好處是當你新增或是刪除一個 li 的時候,不用去處理跟那個元素相關的 listener,因為你的 listener 是放在 ul 身上。這樣透過父節點來處理子節點的事件,就叫做事件代理。
window.addEventListener('click', (e) => {
console.log(e.target);
}, true)
利用事件傳遞機制的特性,在window
上面使用捕獲,就能保證一定是第一個被執行的事件,你就可以在這個 function 裡面偵測頁面中每一個元素的點擊,可以傳回去做數據統計及分析。
資料儲存
cookie
Server 端透過 HTTP response 把資料寫到 cookie
在 header 內的 Set-Cookie 可以存放 cookie
所有的 request 都會自動把瀏覽器的 cookie 帶上去,為了讓瀏覽器可以辨識身份
local storage
const oldValue = windows.localStarge.getItem("text")
document.querySelector(".text").value = oldValue;
document.querySelector('button').addEventListener("click"),
function() {
const value = document.querySelector(".text").value
window.localStorage.setUtem('text', value)
}
session storage
只在這個分頁存在的時候有效,不同的分頁也不能共享
const oldValue = windows.sessionStorage.getItem("text")
document.querySelector(".text").value = oldValue;
document.querySelector('button').addEventListener("click"),
function() {
const value = document.querySelector(".text").value
window.sessionStorage.setUtem('text', value)
}