DOM (文檔對象模型) 詳解
一、DOM 基礎概念
1. 定義與作用
DOM(Document Object Model)即文檔對象模型,是一種用于 HTML 和 XML 文檔的編程接口。它將文檔解析為一個由節點和對象組成的樹狀結構,允許程序和腳本動態訪問、修改文檔的內容、結構和樣式。
2. 核心特點
- 樹形結構:DOM 將文檔表示為節點樹,每個節點可以有子節點。
- 平臺和語言無關:DOM 可以被任何編程語言訪問,如 JavaScript、Python 等。
- 動態交互:通過 DOM,程序可以實時改變文檔的內容和結構。
3. 節點類型
DOM 樹中的節點主要分為以下幾種類型:
- 元素節點(Element Node):表示 HTML 元素,如
<div>
、<p>
等。 - 文本節點(Text Node):表示元素內的文本內容。
- 屬性節點(Attribute Node):表示元素的屬性,如
id
、class
等。 - 文檔節點(Document Node):表示整個文檔,是 DOM 樹的根節點。
- 注釋節點(Comment Node):表示 HTML 中的注釋。
二、DOM 樹結構
1. 節點關系
- 父節點(Parent Node):每個節點(除根節點外)都有一個父節點。
- 子節點(Child Node):一個節點可以有零個或多個子節點。
- 兄弟節點(Sibling Node):共享同一個父節點的節點互為兄弟節點。
- 祖先節點(Ancestor Node):父節點的父節點,依此類推。
- 后代節點(Descendant Node):子節點的子節點,依此類推。
2. 示例 HTML 與 DOM 樹
<!DOCTYPE html>
<html>
<head><title>DOM 示例</title>
</head>
<body><div id="container"><h1>Hello, DOM!</h1><p class="content">這是一個 <a href="#">DOM 示例</a>。</p></div>
</body>
</html>
對應的簡化 DOM 樹結構:
Document
└── html (Element)├── head (Element)│ └── title (Element)│ └── "DOM 示例" (Text)└── body (Element)└── div (Element, id="container")├── h1 (Element)│ └── "Hello, DOM!" (Text)└── p (Element, class="content")├── "這是一個 " (Text)└── a (Element, href="#")└── "DOM 示例" (Text)└── "。" (Text)
三、JavaScript 中的 DOM 操作
1. 訪問 DOM 元素
通過 ID 訪問
const element = document.getElementById('container');
通過標簽名訪問
const paragraphs = document.getElementsByTagName('p');
// 返回 HTMLCollection 對象
通過類名訪問
const elements = document.getElementsByClassName('content');
// 返回 HTMLCollection 對象
通過選擇器訪問
const element = document.querySelector('#container p');
// 返回匹配的第一個元素const elements = document.querySelectorAll('p.content');
// 返回 NodeList 對象
2. 操作元素內容
修改文本內容
const heading = document.querySelector('h1');
heading.textContent = '新標題';
修改 HTML 內容
const container = document.getElementById('container');
container.innerHTML = '<p>新內容</p>';
3. 操作元素屬性
獲取屬性值
const link = document.querySelector('a');
const href = link.getAttribute('href');
設置屬性值
link.setAttribute('href', 'https://example.com');
link.setAttribute('target', '_blank');
直接訪問屬性
link.href = 'https://example.com';
link.target = '_blank';
4. 操作元素樣式
內聯樣式
const element = document.getElementById('container');
element.style.backgroundColor = 'lightblue';
element.style.width = '500px';
類操作
element.classList.add('active');
element.classList.remove('hidden');
element.classList.toggle('highlight');
5. 創建和修改元素
創建新元素
const newDiv = document.createElement('div');
newDiv.textContent = '新創建的元素';
添加子元素
const parent = document.getElementById('container');
parent.appendChild(newDiv);
插入元素
const referenceElement = document.querySelector('p');
parent.insertBefore(newDiv, referenceElement);
刪除元素
parent.removeChild(referenceElement);
替換元素
const newElement = document.createElement('span');
parent.replaceChild(newElement, referenceElement);
6. 事件處理
事件監聽
const button = document.getElementById('myButton');
button.addEventListener('click', function() {alert('按鈕被點擊了!');
});
事件對象
button.addEventListener('click', function(event) {console.log('事件類型:', event.type);console.log('觸發元素:', event.target);
});
事件冒泡與捕獲
- 冒泡(Bubbling):事件從觸發元素向上傳播到父元素。
- 捕獲(Capturing):事件從文檔根向下傳播到觸發元素。
// 使用捕獲階段
element.addEventListener('click', function() {// ...
}, true);
阻止事件傳播
event.stopPropagation();
阻止默認行為
event.preventDefault();
四、DOM 性能優化
1. 減少 DOM 操作
批量修改
// 低效
const list = document.getElementById('myList');
for (let i = 0; i < 1000; i++) {const item = document.createElement('li');item.textContent = `Item ${i}`;list.appendChild(item);
}// 高效
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {const item = document.createElement('li');item.textContent = `Item ${i}`;fragment.appendChild(item);
}
list.appendChild(fragment);
2. 使用事件委托
// 為大量按鈕添加事件
document.getElementById('buttonContainer').addEventListener('click', function(event) {if (event.target.tagName === 'BUTTON') {console.log('按鈕被點擊:', event.target.textContent);}
});
3. 緩存 DOM 引用
// 低效
for (let i = 0; i < 1000; i++) {document.getElementById('myElement').style.left = i + 'px';
}// 高效
const element = document.getElementById('myElement');
for (let i = 0; i < 1000; i++) {element.style.left = i + 'px';
}
4. 避免頻繁重排和重繪
重排(Reflow)
當 DOM 的變化影響了元素的布局信息時,瀏覽器需要重新計算元素的布局,將其安放在界面中的正確位置。
重繪(Repaint)
當一個元素的外觀發生改變,但沒有影響到布局信息時,瀏覽器會將新樣式應用到元素上。
優化建議
- 批量修改樣式
- 使用 documentFragment
- 避免頻繁讀取布局信息
- 使用 transform 和 opacity 進行動畫
五、DOM 與 XML
1. XML DOM
DOM 最初是為 XML 設計的,同樣適用于 HTML。XML DOM 提供了與 HTML DOM 類似的 API 來操作 XML 文檔。
2. XML 解析示例
const xmlText = '<book><title>JavaScript DOM</title><author>John Doe</author></book>';
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, 'text/xml');const title = xmlDoc.querySelector('title').textContent;
console.log('書名:', title);
六、DOM 標準
1. DOM 級別
- DOM Level 1:1998 年發布,定義了基本的 DOM 結構和操作。
- DOM Level 2:2000 年發布,增加了事件模型、樣式、遍歷和范圍等模塊。
- DOM Level 3:2004 年發布,增加了文檔驗證、加載和保存等功能。
- DOM Living Standard:W3C 持續更新的 DOM 標準。
2. DOM API 分類
- 核心 DOM:通用的 DOM 操作接口。
- HTML DOM:專門針對 HTML 的擴展。
- SVG DOM:針對 SVG 圖形的擴展。
- XML DOM:針對 XML 文檔的擴展。
七、高級 DOM 技術
1. 節點遍歷
const element = document.getElementById('container');
let node = element.firstChild;while (node) {console.log('節點類型:', node.nodeType);console.log('節點內容:', node.textContent);node = node.nextSibling;
}
2. 范圍(Range)
const range = document.createRange();
range.selectNode(document.getElementById('myElement'));// 復制范圍內容
const fragment = range.cloneContents();
document.body.appendChild(fragment);
3. Mutation Observer
監聽 DOM 變化的現代 API。
const observer = new MutationObserver((mutations) => {mutations.forEach((mutation) => {console.log('DOM 變化類型:', mutation.type);});
});const options = {childList: true,attributes: true,subtree: true
};observer.observe(document.body, options);
八、DOM 與性能監控
1. 使用 Performance API
// 測量 DOM 操作時間
console.time('dom操作');// DOM 操作
const element = document.createElement('div');
document.body.appendChild(element);console.timeEnd('dom操作');
2. Chrome DevTools 性能分析
- 使用 Performance 面板記錄和分析頁面性能。
- 關注 Layout(重排)和 Paint(重繪)事件。
九、DOM 安全問題
1. XSS 攻擊
- 惡意代碼通過未過濾的用戶輸入注入到 DOM 中。
- 防范措施:對用戶輸入進行過濾和轉義。
2. 安全編碼實踐
// 不安全
const userInput = '<script>alert("XSS");</script>';
document.getElementById('output').innerHTML = userInput;// 安全
const textNode = document.createTextNode(userInput);
document.getElementById('output').appendChild(textNode);
十、DOM 的未來發展
1. Web Components
- 使用 Shadow DOM 創建封裝的組件。
- 減少全局命名空間污染。
2. 虛擬 DOM
- React、Vue 等框架使用虛擬 DOM 提高性能。
- 減少直接操作真實 DOM 的頻率。
通過深入理解 DOM,開發者可以更高效地操作網頁內容,優化性能,并創建出交互性更強的 Web 應用。