DOM入門,理解文檔對(duì)象模型
一、什么是DOM?
DOM全稱是Document Object Model(文檔對(duì)象模型), 它是HTML和XML文檔的編程接口. 簡(jiǎn)單來(lái)說(shuō), DOM就是把我們的網(wǎng)頁(yè)文檔轉(zhuǎn)換成了一個(gè)對(duì)象模型, 這樣我們就可以用JavaScript來(lái)操作網(wǎng)頁(yè)了.
想象一下: 你寫了一個(gè)HTML文件, 瀏覽器加載這個(gè)文件后, 會(huì)把它解析成一個(gè)樹狀結(jié)構(gòu), 這個(gè)結(jié)構(gòu)就是DOM. 通過(guò)DOM, 我們可以:
- 改變網(wǎng)頁(yè)的內(nèi)容.
- 修改元素的樣式.
- 添加或刪除元素.
- 響應(yīng)用戶的交互.
示例代碼:
//舉個(gè)最簡(jiǎn)單的例子
document.getElementById('myButton').addEventListener('click', function() {
alert('按鈕被點(diǎn)擊了!');
});
二、DOM樹結(jié)構(gòu)解析
DOM的核心概念是"樹結(jié)構(gòu)", 讓我們用一個(gè)簡(jiǎn)單的HTML例子來(lái)說(shuō)明:
<!DOCTYPE html>
<html>
<head>
<title>我的網(wǎng)頁(yè)</title>
</head>
<body>
<h1>歡迎來(lái)到我的網(wǎng)站</h1>
<div class="content">
<p>這是一個(gè)段落</p>
<p>這是另一個(gè)段落</p>
</div>
</body>
</html>
這個(gè)HTML會(huì)被瀏覽器解析成如下的DOM樹:
文檔 (Document)
└── html (根元素)
├── head
│ └── title
│ └── "我的網(wǎng)頁(yè)" (文本節(jié)點(diǎn))
└── body
├── h1
│ └── "歡迎來(lái)到我的網(wǎng)站" (文本節(jié)點(diǎn))
└── div (class="content")
├── p
│ └── "這是一個(gè)段落" (文本節(jié)點(diǎn))
└── p
└── "這是另一個(gè)段落" (文本節(jié)點(diǎn))
這個(gè)樹狀結(jié)構(gòu)有幾個(gè)關(guān)鍵點(diǎn):
- 文檔節(jié)點(diǎn)(Document): 這是整個(gè)DOM樹的根節(jié)點(diǎn),代表整個(gè)文檔.
- 元素節(jié)點(diǎn)(Element): 如<html>, <head>, <body>, <div>等.
- 文本節(jié)點(diǎn)(Text): 如標(biāo)簽內(nèi)的文字內(nèi)容.
- 屬性節(jié)點(diǎn)(Attribute): 如class="content".
三、節(jié)點(diǎn)類型詳解
1) 元素節(jié)點(diǎn)
這是最常見的節(jié)點(diǎn)類型, 對(duì)應(yīng)HTML標(biāo)簽. 比如<div>, <p>, <a>等.
const divElement = document.createElement('div');
console.log(divElement.nodeType); // 1
console.log(divElement.nodeType === Node.ELEMENT_NODE); // true
2) 文本節(jié)點(diǎn)
包含元素的文本內(nèi)容.
const textNode = document.createTextNode('你好,世界!');
console.log(textNode.nodeType); // 3
3) 注釋節(jié)點(diǎn)
對(duì)應(yīng)HTML中的注釋.
const comment = document.createComment('這是一個(gè)注釋');
console.log(comment.nodeType); // 8
4)文檔節(jié)點(diǎn)
代表整個(gè)文檔, 即document對(duì)象.
console.log(document.nodeType); // 9
5) 節(jié)點(diǎn)之間的關(guān)系
DOM節(jié)點(diǎn)之間有多種關(guān)系:
- parentNode: 父節(jié)點(diǎn).
- childNodes: 所有子節(jié)點(diǎn)的列表(包括文本節(jié)點(diǎn)等).
- children: 僅元素子節(jié)點(diǎn).
- firstChild/lastChild: 第一個(gè)/最后一個(gè)子節(jié)點(diǎn).
- nextSibling/previousSibling: 下一個(gè)/上一個(gè)兄弟節(jié)點(diǎn).
我們來(lái)看個(gè)示例:
<div id="container">
<p>段落1</p>
<p>段落2</p>
</div>
const container = document.getElementById('container');
console.log(container.childNodes.length); //可能為5(包括文本節(jié)點(diǎn)和元素節(jié)點(diǎn))
console.log(container.children.length); //2(只有p元素)
四、DOM操作基礎(chǔ)
1) 查詢?cè)?/h4>// 通過(guò)ID獲取
const element = document.getElementById('myId');
// 通過(guò)類名獲?。ǚ祷豀TMLCollection)
const elements = document.getElementsByClassName('myClass');
// 通過(guò)標(biāo)簽名獲取(返回HTMLCollection)
const divs = document.getElementsByTagName('div');
// 通過(guò)CSS選擇器獲?。ǚ祷氐谝粋€(gè)匹配元素)
const item = document.querySelector('.menu-item');
// 通過(guò)CSS選擇器獲取所有匹配元素(返回NodeList)
const items = document.querySelectorAll('.menu-item');
2)創(chuàng)建與添加元素
// 創(chuàng)建新元素
const newDiv = document.createElement('div');
newDiv.textContent = '我是新創(chuàng)建的div';
// 添加到文檔中
document.body.appendChild(newDiv);
// 插入到特定位置
const container = document.getElementById('container');
const firstChild = container.firstChild;
container.insertBefore(newDiv, firstChild);
3) 修改元素
// 修改內(nèi)容
element.textContent = '新文本內(nèi)容';
element.innerHTML = '<strong>加粗文本</strong>';
// 修改樣式
element.style.color = 'red';
element.style.backgroundColor = '#f0f0f0';
// 修改類
element.classList.add('new-class');
element.classList.remove('old-class');
element.classList.toggle('active');
4)刪除元素
// 移除子元素
const parent = document.getElementById('parent');
const child = document.getElementById('child');
parent.removeChild(child);
// 現(xiàn)代寫法(不需要知道父元素)
child.remove();
五、DOM事件處理
// 通過(guò)ID獲取
const element = document.getElementById('myId');
// 通過(guò)類名獲?。ǚ祷豀TMLCollection)
const elements = document.getElementsByClassName('myClass');
// 通過(guò)標(biāo)簽名獲取(返回HTMLCollection)
const divs = document.getElementsByTagName('div');
// 通過(guò)CSS選擇器獲?。ǚ祷氐谝粋€(gè)匹配元素)
const item = document.querySelector('.menu-item');
// 通過(guò)CSS選擇器獲取所有匹配元素(返回NodeList)
const items = document.querySelectorAll('.menu-item');
// 創(chuàng)建新元素
const newDiv = document.createElement('div');
newDiv.textContent = '我是新創(chuàng)建的div';
// 添加到文檔中
document.body.appendChild(newDiv);
// 插入到特定位置
const container = document.getElementById('container');
const firstChild = container.firstChild;
container.insertBefore(newDiv, firstChild);
// 修改內(nèi)容
element.textContent = '新文本內(nèi)容';
element.innerHTML = '<strong>加粗文本</strong>';
// 修改樣式
element.style.color = 'red';
element.style.backgroundColor = '#f0f0f0';
// 修改類
element.classList.add('new-class');
element.classList.remove('old-class');
element.classList.toggle('active');
// 移除子元素
const parent = document.getElementById('parent');
const child = document.getElementById('child');
parent.removeChild(child);
// 現(xiàn)代寫法(不需要知道父元素)
child.remove();
基本事件處理:
const button = document.getElementById('myButton');
// 傳統(tǒng)方式
button.onclick = function() {
console.log('按鈕被點(diǎn)擊了!');
};
// 推薦方式(可以添加多個(gè)事件監(jiān)聽器)
button.addEventListener('click', function(event) {
console.log('按鈕被點(diǎn)擊了!', event);
});
事件冒泡和捕獲, DOM事件有三個(gè)階段:
- 捕獲階段(從window向下到目標(biāo)元素).
- 目標(biāo)階段(到達(dá)目標(biāo)元素).
- 冒泡階段(從目標(biāo)元素向上冒泡).
document.getElementById('outer').addEventListener('click', function() {
console.log('外層div - 冒泡階段');
}, false); // false或不傳表示冒泡階段
document.getElementById('inner').addEventListener('click', function() {
console.log('內(nèi)層div - 捕獲階段');
}, true); // true表示捕獲階段
利用事件冒泡機(jī)制,可以在父元素上處理子元素的事件.
<ul id="myList">
<li>項(xiàng)目1</li>
<li>項(xiàng)目2</li>
<li>項(xiàng)目3</li>
</ul>
document.getElementById('myList').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
console.log('點(diǎn)擊了:', event.target.textContent);
}
});
六、DOM標(biāo)準(zhǔn)與綜合示例
DOM級(jí)別:
- DOM Level 1 (1998): 定義了核心DOM和HTML DOM.
- DOM Level 2 (2000): 添加了事件模型、樣式訪問等.
- DOM Level 3 (2004): 擴(kuò)展了DOM核心,添加了XPath等.
- DOM Level 4 (2015): 也稱為DOM Living Standard,現(xiàn)代瀏覽器主要實(shí)現(xiàn).
兼容性問題, 雖然現(xiàn)代瀏覽器對(duì)DOM的支持已經(jīng)很好,但仍有一些需要注意的差異:
- IE的差異:IE8及以下不支持addEventListener, 需要使用attachEvent.IE對(duì)某些CSS屬性的訪問方式不同.
- 方法可用性:remove()方法在舊瀏覽器中不可用.classList在IE9及以下不支持.
- 事件對(duì)象:在IE中, 事件對(duì)象是全局的window.event.在其他瀏覽器中, 事件對(duì)象作為參數(shù)傳遞.
兼容性的操作示例:
//兼容性事件處理示例
function addEvent(element, eventName, handler) {
if (element.addEventListener) {
element.addEventListener(eventName, handler);
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, handler);
} else {
element['on' + eventName] = handler;
}
}
//兼容性事件對(duì)象獲取
element.onclick = function(event) {
event = event || window.event;
const target = event.target || event.srcElement;
// 處理事件...
};
綜合示例, 構(gòu)建一個(gè)簡(jiǎn)單的待辦事項(xiàng)列表:
<!DOCTYPE html>
<html>
<head>
<title>待辦事項(xiàng)</title>
<style>
.completed {
text-decoration: line-through;
color: #999;
}
</style>
</head>
<body>
<h1>我的待辦事項(xiàng)</h1>
<input type="text" id="newTodo" placeholder="輸入新任務(wù)">
<button id="addBtn">添加</button>
<ul id="todoList"></ul>
<script>
document.addEventListener('DOMContentLoaded', function() {
const newTodoInput = document.getElementById('newTodo');
const addBtn = document.getElementById('addBtn');
const todoList = document.getElementById('todoList');
// 添加新任務(wù)
function addTodo() {
const text = newTodoInput.value.trim();
if (text) {
const li = document.createElement('li');
const span = document.createElement('span');
span.textContent = text;
const deleteBtn = document.createElement('button');
deleteBtn.textContent = '刪除';
li.appendChild(span);
li.appendChild(deleteBtn);
todoList.appendChild(li);
newTodoInput.value = '';
// 添加點(diǎn)擊事件(完成任務(wù))
span.addEventListener('click', function() {
span.classList.toggle('completed');
});
// 添加刪除按鈕事件
deleteBtn.addEventListener('click', function() {
li.remove();
});
}
}
// 點(diǎn)擊按鈕添加
addBtn.addEventListener('click', addTodo);
// 按回車鍵添加
newTodoInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
addTodo();
}
});
});
</script>
</body>
</html>
這個(gè)例子展示了:
- 動(dòng)態(tài)創(chuàng)建DOM元素
- 事件監(jiān)聽
- 類名切換
- 元素刪除
- 表單處理