DOM節點操作語法知識點及案例詳解
一、語法知識點
1. 獲取節點
// 通過ID獲取
const element = document.getElementById('idName');// 通過類名獲取(返回HTMLCollection)
const elements = document.getElementsByClassName('className');// 通過標簽名獲取(返回HTMLCollection)
const tags = document.getElementsByTagName('div');// 通過CSS選擇器獲取單個元素
const el = document.querySelector('.selector');// 通過CSS選擇器獲取所有匹配元素(返回NodeList)
const nodes = document.querySelectorAll('div.item');// 特殊節點獲取
document.documentElement; // 獲取html元素
document.head; // 獲取head元素
document.body; // 獲取body元素
2. 創建節點
// 創建元素節點
const newDiv = document.createElement('div');// 創建文本節點
const textNode = document.createTextNode('Hello World');// 創建文檔片段(優化批量操作)
const fragment = document.createDocumentFragment();
3. 添加節點
// 末尾追加
parentNode.appendChild(childNode);// 指定位置插入
parentNode.insertBefore(newNode, referenceNode);// 插入HTML字符串(更靈活)
parentNode.insertAdjacentHTML('beforeend', '<div>New item</div>');
/*
位置參數:
'beforebegin':元素自身的前面
'afterbegin':插入元素內部的第一個子節點之前
'beforeend':插入元素內部的最后一個子節點之后
'afterend':元素自身的后面
*/
4. 刪除節點
// 傳統方法(需要父節點)
parentNode.removeChild(childNode);// 現代方法(直接操作)
childNode.remove();// 清空所有子節點
while (container.firstChild) {container.removeChild(container.firstChild);
}
5. 克隆節點
const cloneNode = originalNode.cloneNode(true); // true表示深度克隆
6. 節點關系
node.parentNode; // 父節點
node.childNodes; // 子節點集合
node.firstChild; // 第一個子節點
node.lastChild; // 最后一個子節點
node.previousSibling; // 前一個兄弟節點
node.nextSibling; // 后一個兄弟節點
二、綜合案例
案例1:線上點菜系統
<!-- HTML結構 -->
<div class="container"><div class="menu"><h3>菜單列表</h3><ul id="foodList"><li>魚香肉絲 <span class="price">¥28</span></li><li>宮保雞丁 <span class="price">¥32</span></li><li>麻婆豆腐 <span class="price">¥18</span></li></ul></div><div class="ordered"><h3>已點菜品 <span id="total">總計:¥0</span></h3><ul id="orderedList"></ul></div>
</div><script>
// 初始化總價
let totalPrice = 0;// 菜單點擊事件委托
document.getElementById('foodList').addEventListener('click', function(e) {if (e.target.tagName === 'LI') {// 克隆選中的菜品const clonedItem = e.target.cloneNode(true);// 添加刪除按鈕const delBtn = document.createElement('button');delBtn.textContent = '×';delBtn.className = 'delete-btn';clonedItem.appendChild(delBtn);// 添加到已點列表document.getElementById('orderedList').appendChild(clonedItem);// 更新總價const price = parseFloat(e.target.querySelector('.price').textContent.slice(1));totalPrice += price;updateTotal();}
});// 刪除功能委托
document.getElementById('orderedList').addEventListener('click', function(e) {if (e.target.classList.contains('delete-btn')) {const listItem = e.target.parentElement;const price = parseFloat(listItem.querySelector('.price').textContent.slice(1));// 移除元素并更新總價listItem.remove();totalPrice -= price;updateTotal();}
});function updateTotal() {document.getElementById('total').textContent = `總計:¥${totalPrice.toFixed(2)}`;
}
</script>
案例2:電商購物車
<!-- HTML結構 -->
<div class="cart"><h2>購物車 <span id="cartTotal">¥0.00</span></h2><ul id="cartList"></ul><button id="addItem">添加商品</button>
</div><script>
class ShoppingCart {constructor() {this.items = [];this.cart = document.getElementById('cartList');this.init();}init() {// 添加示例商品this.addItem('iPhone 15', 7999);this.addItem('AirPods Pro', 1499);// 事件委托處理所有操作this.cart.addEventListener('click', (e) => {const itemEl = e.target.closest('.cart-item');if (!itemEl) return;const id = itemEl.dataset.id;const item = this.items.find(i => i.id == id);if (e.target.classList.contains('quantity-up')) {item.quantity++;} else if (e.target.classList.contains('quantity-down')) {if (item.quantity > 1) item.quantity--;} else if (e.target.classList.contains('delete-btn')) {this.removeItem(id);}this.updateItem(itemEl, item);this.updateTotal();});document.getElementById('addItem').addEventListener('click', () => {const name = prompt('請輸入商品名稱:');const price = parseFloat(prompt('請輸入商品價格:'));if (name && price) this.addItem(name, price);});}addItem(name, price) {const newItem = {id: Date.now(),name,price,quantity: 1};this.items.push(newItem);this.renderItem(newItem);this.updateTotal();}renderItem(item) {const li = document.createElement('li');li.className = 'cart-item';li.dataset.id = item.id;li.innerHTML = `<span class="name">${item.name}</span><div class="controls"><button class="quantity-down">-</button><span class="quantity">${item.quantity}</span><button class="quantity-up">+</button></div><span class="price">¥${(item.price * item.quantity).toFixed(2)}</span><button class="delete-btn">刪除</button>`;this.cart.appendChild(li);}updateItem(el, item) {el.querySelector('.quantity').textContent = item.quantity;el.querySelector('.price').textContent = `¥${(item.price * item.quantity).toFixed(2)}`;}removeItem(id) {this.items = this.items.filter(item => item.id != id);document.querySelector(`[data-id="${id}"]`).remove();}updateTotal() {const total = this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);document.getElementById('cartTotal').textContent = `¥${total.toFixed(2)}`;}
}// 初始化購物車
new ShoppingCart();
</script>
三、關鍵知識點解析
-
事件委托:兩個案例都使用了事件委托處理動態元素的事件,通過檢查event.target來識別操作源
-
數據驅動:購物車案例將數據與DOM分離,保持數據源(this.items)與視圖同步
-
克隆節點:點菜案例使用cloneNode(true)復制完整DOM結構
-
元素定位:
- closest() 方法查找最近的匹配祖先元素
- dataset 屬性操作自定義數據屬性
-
數值處理:
- 使用toFixed(2)保持貨幣格式
- 使用parseFloat處理價格轉換
-
批量操作優化:
- 文檔片段(document.createDocumentFragment)
- 減少DOM操作次數(先計算后更新)
這些案例涵蓋了DOM操作的核心知識點,實際開發中可結合CSS實現樣式優化,并考慮添加本地存儲功能實現數據持久化。