放自己用DOM寫的作品鏈接
寫于2017.08.07
一、關(guān)于DOM
- 什么是DOM
DOM是JavaScript操作網(wǎng)頁的接口,全稱為“文檔對象模型”(Document Object Model)。它的作用是將網(wǎng)頁轉(zhuǎn)為一個JavaScript對象,從而可以用腳本進(jìn)行各種操作(比如增刪內(nèi)容)。
瀏覽器會根據(jù)DOM模型,將結(jié)構(gòu)化文檔(比如HTML和XML)解析成一系列的節(jié)點,再由這些節(jié)點組成一個樹狀結(jié)構(gòu)(DOM Tree)。所有的節(jié)點和最終的樹狀結(jié)構(gòu),都有規(guī)范的對外接口。所以,DOM可以理解成網(wǎng)頁的編程接口。
嚴(yán)格地說,DOM不屬于JavaScript,但是操作DOM是JavaScript最常見的任務(wù),而JavaScript也是最常用于DOM操作的語言 - DOM的節(jié)點
DOM的最小組成單位叫做節(jié)點(node)。文檔的樹形結(jié)構(gòu)(DOM樹),就是由各種不同類型的節(jié)點組成。每個節(jié)點可以看作是文檔樹的一片葉子。
節(jié)點的類型有七種。
*Document:整個文檔樹的頂層節(jié)點
*DocumentType:doctype標(biāo)簽(比如<!DOCTYPE html>)
*Element:網(wǎng)頁的各種HTML標(biāo)簽(比如<body>、<a>等)
*Attribute:網(wǎng)頁元素的屬性(比如class="right")
*Text:標(biāo)簽之間或標(biāo)簽包含的文本
*Comment:注釋
*DocumentFragment:文檔的片段
DOM樹如下圖:

具體DOM學(xué)習(xí)建議參考鏈接:http://javascript.ruanyifeng.com/#dom
二、事件模型
什么是事件
事件是一種異步編程的實現(xiàn)方式,本質(zhì)上是程序各個組成部分之間的通信。一般來講,有一下特點:
1、某某訂閱了/關(guān)注/監(jiān)聽了×××
2、×××發(fā)生變化
3、某某得到通知
體現(xiàn)在DOM機(jī)制里就是:用戶的操作發(fā)生變化,代碼得到通知事件EventTarget接口
DOM的事件操作(監(jiān)聽和觸發(fā)),都定義在EventTarget接口。Element節(jié)點、document節(jié)點和window對象,都部署了這個接口。此外,XMLHttpRequest、AudioNode、AudioContext等瀏覽器內(nèi)置對象,也部署了這個接口。
該接口就是3個方法:
1、addEventListener:綁定事件的監(jiān)聽函數(shù)
例:
target.addEventListener(type, listener[, useCapture]);
type:事件名稱,大小寫敏感。
listener:監(jiān)聽函數(shù)。事件發(fā)生時,會調(diào)用該監(jiān)聽函數(shù)。
useCapture:布爾值,默認(rèn)為false(監(jiān)聽函數(shù)只在冒泡階段被觸發(fā))。true是在捕獲階段觸發(fā)
addEventListener方法可以為當(dāng)前對象的同一個事件,添加多個監(jiān)聽函數(shù)。這些函數(shù)按照添加順序觸發(fā),即先添加先觸發(fā)。如果為同一個事件多次添加同一個監(jiān)聽函數(shù),該函數(shù)只會執(zhí)行一次,多余的添加將自動被去除(不必使用removeEventListener方法手動去除)。
2、removeEventListener:移除事件的監(jiān)聽函數(shù)
例:
div.addEventListener('click', listener, false);
removeEventListener方法移除的監(jiān)聽函數(shù),必須與對應(yīng)的addEventListener方法的參數(shù)完全一致,而且必須在同一個元素節(jié)點,否則無效。
3、dispatchEvent:觸發(fā)事件
dispatchEvent方法在當(dāng)前節(jié)點上觸發(fā)指定事件,從而觸發(fā)監(jiān)聽函數(shù)的執(zhí)行。該方法返回一個布爾值,只要有一個監(jiān)聽函數(shù)調(diào)用了Event.preventDefault(),則返回值為false,否則為true
例:
para.addEventListener('click', hello, false);
var event = new Event('click');
para.dispatchEvent(event);
上面代碼在當(dāng)前節(jié)點觸發(fā)了click事件-
3種綁定監(jiān)聽函數(shù)的方法
1、HTML標(biāo)簽的on-屬性
例:元素標(biāo)簽的屬性中,直接定義某些事件的監(jiān)聽代碼
<body onload="doSomething()">
<div onclick="console.log('觸發(fā)事件')">
上面代碼為body節(jié)點的load事件、div節(jié)點的click事件,指定了監(jiān)聽函數(shù)。
使用這個方法指定的監(jiān)聽函數(shù),只會在冒泡階段觸發(fā)另外,Element元素節(jié)點的setAttribute方法,其實設(shè)置的也是這種效果。
el.setAttribute('onclick', 'doSomething()');
2、Element節(jié)點的事件屬性
Element節(jié)點對象有事件屬性,同樣可以指定監(jiān)聽函數(shù)。
例:
window.onload = doSomething;
div.onclick = function(event){
console.log('觸發(fā)事件');
};
使用這個方法指定的監(jiān)聽函數(shù),只會在冒泡階段觸發(fā)。
3、addEventListener方法
window.addEventListener('load', doSomething, false);(詳解如上)
總結(jié):第一種“HTML標(biāo)簽的on-屬性”,違反了HTML與JavaScript代碼相分離的原則;第二種“Element節(jié)點的事件屬性”的缺點是,同一個事件只能定義一個監(jiān)聽函數(shù),也就是說,如果定義兩次onclick屬性,后一次定義會覆蓋前一次。 事件的捕獲與冒泡階段
1、操作系統(tǒng)最先知道用戶點擊了鼠標(biāo),瀏覽器次之
2、child被點擊了,意味著parent也被點擊了
3、那么如果同時監(jiān)聽了child和parent,誰先通知我
例子:如果對parent和child同時設(shè)置了監(jiān)聽,那么當(dāng)我點擊child,誰先通知我
捕獲階段:parent先通知,child后通知(一般不用)
冒泡階段:child先通知,parent后通知e.target與e.currentTarget
e.target代表你點擊的那個元素,而e.currentTarget代表你監(jiān)聽的那個元素,如果你處于捕獲或者冒泡階段,二者是不一樣的阻止默認(rèn)事件
document.querySelector("a").addEventListener('click',function(e){
e.preventDefault()
})
這樣的話a就不會跳轉(zhuǎn)了。如果在父元素阻止默認(rèn)事件,那a也不跳轉(zhuǎn)了,所以一般不建議這樣做停止冒泡
e.stopPropagation( )(在子元素加入,則不通知爸爸了,不會再向上傳播)事件委托
由于事件會在冒泡階段向上傳播到父節(jié)點,因此可以把子節(jié)點的監(jiān)聽函數(shù)定義在父節(jié)點上,由父節(jié)點的監(jiān)聽函數(shù)統(tǒng)一處理多個子元素的事件。這種方法叫做事件委托
例:
HTML代碼:
<ul>
<li>選項1</li>
<li>選項2</li>
<li>選項3</li>
<li>選項4</li>
</ul>
JS代碼:
var ul =document.querySelector('ul')
function fn(e){
if(e.target.tagName ==="LI"){
console.log("ok")(如果li里面裹一層span,點擊span就不能執(zhí)行監(jiān)聽函數(shù))
}
}
ul.addEventListener('click',fn)
優(yōu)化執(zhí)行函數(shù):
function fn(e){
var el = e.target
while(el.tagName !=="LI"){
el =el.parentNode(向上遍歷父元素)
}
if(el){
console.log('yes')
}
}
但是實際上我只需要遍歷到我監(jiān)聽的元素就可以了,如果直到遍歷到監(jiān)聽的元素還沒有的話,就認(rèn)為沒有了
例:
HTML代碼:
<div>
<p>我是<span>p</span></p>
<hi>我是<span>h1</span></hi>
</div>
JS代碼:
var div =document.querySelector('div')
function fn(e){
var el = e.target
while(el.tagName !=="H1"){
el =el.parentNode(向上遍歷父元素)
if(el===div){
el=null
break;
}
}
if(el){
console.log('yes')
}
}
div.addEventListener('click',fn(e))