DOM(Document Object Model)操作文件物件模型,是的!繞來繞去還是物件,所以使用過程中不難發現都是用第 5 天:物件與陣列物件,通過對這些物件的屬性和方法進行操作來改變頁面內容或樣式。
DOM 的基本概念
如果將 DOM 轉換為圖表理解,會以類似「樹狀關係圖」的樣貌展現,因此常常聽見 DOM Tree 也代表 DOM 架構資料的樣貌,和 HTML 一樣有階層關係,不過使用物件來呈現。
圖片來源:DOM API 一篇就會!
DOM 的應用
動態內容更新:
利用 DOM,開發者可以在頁面加載後動態更新內容,比如顯示用戶提交的表單數據。
<form id="myForm">
<input type="text" id="userInput" placeholder="輸入一些內容" required >
<button type="submit">提交</button>
</form>
<div id="output" style="margin: 1rem;" />
/** 取 DOM */
const form = document.getElementById('myForm');
const output = document.getElementById('output');
/** 事件 */
form.addEventListener('submit', function (event) {
// 阻止默認提交
event.preventDefault();
const userInput = document.getElementById('userInput').value;
// 更新內容
output.textContent = `你輸入的內容是:${userInput}`;
});
操作樣式:
可以使用 JavaScript 修改元素的 CSS 樣式,以便實現動畫或其他視覺效果。
<div id="myBox" />
<button type="button" id="changeStyle">改變樣式</button>
#myBox {
width: 100px;
height: 100px;
background-color: blue;
transition: all 0.5s;
}
/** 取 DOM */
const box = document.getElementById('myBox');
const button = document.getElementById('changeStyle');
/** 事件 */
button.addEventListener('click', function () {
box.style.backgroundColor = 'red'; // 改變顏色
box.style.width = '200px'; // 改變寬度
box.style.height = '200px'; // 改變高度
});
增刪節點:
可以動態增加或刪除 HTML 元素,例如從列表中移除一個項目。
<ul id="itemList">
<li>項目 1 <button type="button" class="remove">刪除</button></li>
<li>項目 2 <button type="button" class="remove">刪除</button></li>
<li>項目 3 <button type="button" class="remove">刪除</button></li>
</ul>
const itemList = document.getElementById('itemList');
itemList.addEventListener('click', function (event) {
if (event.target.classList.contains('remove')) {
const listItem = event.target.parentElement;
itemList.removeChild(listItem); // 刪除項目
}
});
資料綁定:
與框架(如 Vue、React 等)結合使用時,DOM 可以與數據模型綁定,自動更新界面。
<script setup>
import { ref } from 'vue'
const message = ref('');
</script>
<template>
<input v-model="message" placeholder="輸入消息" />
<p>你輸入的消息是:{{ message }}</p>
</template>
基本概念
使用者瀏覽網頁的行為會觸發許多「事件」,像是按下按鍵、移動滑鼠、點擊網頁元素,JavaScript 讓前端能夠對這些事件進行監聽和響應。
常見的事件型態包含 click、change、mousemove 等等,可以參考 MDN Event reference 了解更多事件型態。
MDN 文件:Event reference
事件生命週期
觸發:用戶與元素互動,例如點擊按鈕。
捕獲:事件從 window 開始,向下傳遞到目標元素。
目標:事件到達目標元素,此時會執行與該事件相關的處理程序。
冒泡:事件從目標元素向上傳遞,直到 window。
addEventListener
和事件委託addEventListener
addEventListener
方法用於將事件監聽器附加到指定的 DOM 元素。
target.addEventListener(event, handler, options);
event
:handler
:觸發事件時所執行的函式。options
:選擇性加入一些額外的事件監聽設定。const button = document.getElementById('myButton');
button.addEventListener('click', function() {
alert('Button clicked!');
});
🔔 在指定元素(domElement)監聽特定事件(此範例為 click 事件,需使用引號),當該事件觸發時會執行後方函式,函式有一個參數為事件物件
事件委託
事件委託是將事件監聽器附加到父元素上,而不是每個子元素。這樣可以提高性能,特別是當子元素動態生成時。
<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
const list = document.getElementById('myList');
list.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
alert(`You clicked on ${event.target.textContent}`);
}
});
事件物件
當事件發生時,JavaScript 會創建一個事件物件,包含有關事件的信息,如:
type
:事件類型,例如 click
。target
:事件的目標元素。currentTarget
:當前事件處理函數所綁定的元素。preventDefault()
:阻止事件的默認行為。
anchorElement.addEventListener("click", function (event) {
// 阻止連結導航
event.preventDefault();
});
stopPropagation()
:停止事件的捕獲或冒泡。事件處理函數
事件處理函數是響應事件的函數。它通常接收一個事件物件作為參數。
const input = document.getElementById('myInput');
input.addEventListener('input', function(event) {
console.log('Input value:', event.target.value);
});
// 操縱屬性
element.setAttribute("attributeName", "value"); element.getAttribute("attributeName");
element.removeAttribute("attributeName");
// 修改內容
element.innerHTML = "New content";
element.textContent = "New text content";
// 操縱 class
element.classList.add("className");
element.classList.remove("className");
element.classList.toggle("className");
// Event Handling 事件處理
element.addEventListener("click", function);
element.removeEventListener("click", function);
// Window 方法
window.alert("Message");
window.prompt("Prompt message", "default value");
window.confirm("Confirmation message");
Vue 提供了 ref
這個 instance property 的來解決上述問題;只要在透過 ref
這個 attribute 就可以讓 Vue 定位取得你想要的 instance;簡單來說,ref
屬性是可以快速取得 DOM 元素所使用。
💡 補充說明:同樣是 Vue 提供的
ref
在<template>
和<script setup>
有什麼不一樣?<template>
:使用的ref
是一個特殊的屬性,用於獲取對 DOM 元素或子組件的直接引用。<script setup>
:響應式的ref
用於創建響應式數據。在 Vue3 中,使用ref
函數可以將基本類型的值(如字符串、數字)轉換為響應式的引用。
最近剛好實戰上遇到一個很經典的例子可以分享:表格要做拖曳,當然拖曳過程要新增模擬線,讓使用者知道即將拖曳到哪列,也就是提升使用者互動體感!
前面的拖曳不難,重點在不能使用套件要新增模擬線!🙃
透過vue-drag-and-drop知道有現成的 API 可以用,但是...怎麼 console.log 結果都是 DragEvent
本規劃有機會可以抓到 row_index 這樣就方便算進入第幾列然後給固定高度計算給線,問題來了,怎麼從這些訊息中抓到 row_index?
曾經有夥伴跟我說:
你不會給每行都一個 id 唷!心理 os,我抓得到 id 還用煩惱嗎?
不然計算全畫面高度在去 ++--...,心理 os,天呀!饒了我吧!
所以最後解法是用 DOM 抓!
const getRowIndex = (event) => {
const rowElement = event.target.closest('.ivu-table-row');
if (!rowElement) return null
return Array.from(rowElement.parentNode.children).indexOf(rowElement)
}