本篇文章給大家?guī)黻P(guān)于JavaScript進階班之DOM技術(shù)的相關(guān)知識,希望對大家有幫助。
思維導(dǎo)圖、目錄總覽
1、DOM簡介
1.1、什么是DOM
文檔對象模型(Document Object Model,簡稱 DOM),是 W3C 組織推薦的處理可擴展標記語言(HTML或者XML)的標準編程接口
W3C 已經(jīng)定義了一系列的 DOM 接口,通過這些 DOM 接口可以改變網(wǎng)頁的內(nèi)容、結(jié)構(gòu)和樣式。
- 文檔:一個頁面就是一個文檔,DOM中使用doucument來表示
- 元素:頁面中的所有標簽都是元素,DOM中使用 element 表示
- 節(jié)點:網(wǎng)頁中的所有內(nèi)容都是節(jié)點(標簽,屬性,文本,注釋等),DOM中使用node表示
DOM 把以上內(nèi)容都看做是對象
2、獲取元素
2.1、如何獲取頁面元素
DOM在我們實際開發(fā)中主要用來操作元素。
我們?nèi)绾蝸慝@取頁面中的元素呢?
獲取頁面中的元素可以使用以下幾種方式:
- 根據(jù) ID 獲取
- 根據(jù)標簽名獲取
- 通過 HTML5 新增的方法獲取
- 特殊元素獲取
2.2、根據(jù)ID獲取
使用 getElementByld()
方法可以獲取帶ID的元素對象
doucument.getElementByld('id名')
使用 console.dir()
可以打印我們獲取的元素對象,更好的查看對象里面的屬性和方法。
示例
<p id="time">2019-9-9</p><script> // 1.因為我們文檔頁面從上往下加載,所以得先有標簽,所以script寫在標簽下面 // 2.get 獲得 element 元素 by 通過 駝峰命名法 // 3.參數(shù) id是大小寫敏感的字符串 // 4.返回的是一個元素對象 var timer = document.getElementById('time'); console.log(timer); // 5. console.dir 打印我們的元素對象,更好的查看里面的屬性和方法 console.dir(timer);</script>
2.3、根據(jù)標簽名獲取
根據(jù)標簽名獲取,使用 getElementByTagName()
方法可以返回帶有指定標簽名的對象的集合
doucument.getElementsByTagName('標簽名');
- 因為得到的是一個對象的集合,所以我們想要操作里面的元素就需要遍歷
- 得到元素對象是動態(tài)的
- 返回的是獲取過來元素對象的集合,以偽數(shù)組的形式存儲
- 如果獲取不到元素,則返回為空的偽數(shù)組(因為獲取不到對象)
<ul> <li>知否知否,應(yīng)是等你好久</li> <li>知否知否,應(yīng)是等你好久</li> <li>知否知否,應(yīng)是等你好久</li> <li>知否知否,應(yīng)是等你好久</li> <li>知否知否,應(yīng)是等你好久</li></ul><script> // 1.返回的是獲取過來元素對象的集合 以偽數(shù)組的形式存儲 var lis = document.getElementsByTagName('li'); console.log(lis); console.log(lis[0]); // 2.依次打印,遍歷 for (var i = 0; i < lis.length; i++) { console.log(lis[i]); } // 3.如果頁面中只有 1 個 li,返回的還是偽數(shù)組的形式 // 4.如果頁面中沒有這個元素,返回的是空偽數(shù)組</script>
2.4、根據(jù)標簽名獲取
還可以根據(jù)標簽名獲取某個元素(父元素)內(nèi)部所有指定標簽名的子元素,獲取的時候不包括父元素自己
element.getElementsByTagName('標簽名')ol.getElementsByTagName('li');
注意:父元素必須是單個對象(必須指明是哪一個元素對象),獲取的時候不包括父元素自己
<script> //element.getElementsByTagName('標簽名'); 父元素必須是指定的單個元素 var ol = document.getElementById('ol'); console.log(ol.getElementsByTagName('li'));</script>
2.5、通過H5新增方法獲取
①getElementsByClassName
根據(jù)類名返回元素對象合集
document.getElementsByClassName('類名')
document.getElementsByClassName('類名');
②document.querySelector
根據(jù)指定選擇器返回第一個元素對象
document.querySelector('選擇器'); // 切記里面的選擇器需要加符號 // 類選擇器.box // id選擇器 #nav var firstBox = document.querySelector('.box');
③document.querySelectorAll
根據(jù)指定選擇器返回所有元素對象
document.querySelectorAll('選擇器');
注意
querySelector
和 querySelectorAll
里面的選擇器需要加符號,比如: document.querySelector('#nav');
④例子
<script> // 1. getElementsByClassName 根據(jù)類名獲得某些元素集合 var boxs = document.getElementsByClassName('box'); console.log(boxs); // 2. querySelector 返回指定選擇器的第一個元素對象 切記 里面的選擇器需要加符號 .box #nav var firstBox = document.querySelector('.box'); console.log(firstBox); var nav = document.querySelector('#nav'); console.log(nav); var li = document.querySelector('li'); console.log(li); // 3. querySelectorAll()返回指定選擇器的所有元素對象集合 var allBox = document.querySelectorAll('.box'); console.log(allBox); var lis = document.querySelectorAll('li'); console.log(lis);</script>
2.6、獲取特殊元素
①獲取body元素
返回body元素對象
document.body;
②獲取html元素
返回html元素對象
document.documentElement;
3、事件基礎(chǔ)
3.1、事件概述
JavaScript 使我們有能力創(chuàng)建動態(tài)頁面,而事件是可以被 JavaScript 偵測到的行為。
簡單理解: 觸發(fā)— 響應(yīng)機制。
網(wǎng)頁中的每個元素都可以產(chǎn)生某些可以觸發(fā) JavaScript 的事件,例如,我們可以在用戶點擊某按鈕時產(chǎn)生一個事件,然后去執(zhí)行某些操作。
3.2、事件三要素
- 事件源(誰)
- 事件類型(什么事件)
- 事件處理程序(做啥)
<script> // 點擊一個按鈕,彈出對話框 // 1. 事件是有三部分組成 事件源 事件類型 事件處理程序 我們也稱為事件三要素 //(1) 事件源 事件被觸發(fā)的對象 誰 按鈕 var btn = document.getElementById('btn'); //(2) 事件類型 如何觸發(fā) 什么事件 比如鼠標點擊(onclick) 還是鼠標經(jīng)過 還是鍵盤按下 //(3) 事件處理程序 通過一個函數(shù)賦值的方式 完成 btn.onclick = function() { alert('點秋香'); }</script>
3.3、執(zhí)行事件的步驟
- 獲取事件源
- 注冊事件(綁定事件)
- 添加事件處理程序(采取函數(shù)賦值形式)
<script> // 執(zhí)行事件步驟 // 點擊p 控制臺輸出 我被選中了 // 1. 獲取事件源 var p = document.querySelector('p'); // 2.綁定事件 注冊事件 // p.onclick // 3.添加事件處理程序 p.onclick = function() { console.log('我被選中了'); }</script>
3.4、鼠標事件
鼠標事件 | 觸發(fā)條件 |
---|---|
onclick | 鼠標點擊左鍵觸發(fā) |
onmouseover | 鼠標經(jīng)過觸發(fā) |
onmouseout | 鼠標離開觸發(fā) |
onfocus | 獲得鼠標焦點觸發(fā) |
onblur | 失去鼠標焦點觸發(fā) |
onmousemove | 鼠標移動觸發(fā) |
onmouseup | 鼠標彈起觸發(fā) |
onmousedown | 鼠標按下觸發(fā) |
4、操作元素
JavaScript 的 DOM 操作可以改變網(wǎng)頁內(nèi)容、結(jié)構(gòu)和樣式,我們可以利用 DOM 操作元素來改變元素里面的內(nèi)容 、屬性等。注意以下都是屬性
4.1、改變元素內(nèi)容
從起始位置到終止位置的內(nèi)容,但它去除html標簽,同時空格和換行也會去掉。
element.innerText
起始位置到終止位置的全部內(nèi)容,包括HTML標簽,同時保留空格和換行
element.innerHTML
<body> <p></p> <p> 我是文字 <span>123</span> </p> <script> // innerText 和 innerHTML的區(qū)別 // 1. innerText 不識別html標簽,去除空格和換行 var p = document.querySelector('p'); p.innerText = '<strong>今天是:</strong> 2019'; // 2. innerHTML 識別html標簽 保留空格和換行的 p.innerHTML = '<strong>今天是:</strong> 2019'; // 這兩個屬性是可讀寫的 可以獲取元素里面的內(nèi)容 var p = document.querySelector('p'); console.log(p.innerText); console.log(p.innerHTML); </script></body>
4.2、改變元素屬性
// img.屬性 img.src = "xxx"; input.value = "xxx"; input.type = "xxx"; input.checked = "xxx"; input.selected = true / false; input.disabled = true / false;
4.3、改變樣式屬性
我們可以通過 JS 修改元素的大小、顏色、位置等樣式。
- 行內(nèi)樣式操作
// element.stylep.style.backgroundColor = 'pink';p.style.width = '250px';
- 類名樣式操作
// element.className
注意:
- JS里面的樣式采取駝峰命名法,比如 fontSize ,backgroundColor
- JS 修改 style 樣式操作 ,產(chǎn)生的是行內(nèi)樣式,CSS權(quán)重比較高
- 如果樣式修改較多,可以采取操作類名方式更改元素樣式
- class 因為是個保留字,因此使用
className
來操作元素類名屬性 - className 會直接更改元素的類名,會覆蓋原先的類名
<body> <p class="first">文本</p> <script> // 1. 使用 element.style 獲得修改元素樣式 如果樣式比較少 或者 功能簡單的情況下使用 var test = document.querySelector('p'); test.onclick = function() { // this.style.backgroundColor = 'purple'; // this.style.color = '#fff'; // this.style.fontSize = '25px'; // this.style.marginTop = '100px'; // 讓我們當前元素的類名改為了 change // 2. 我們可以通過 修改元素的className更改元素的樣式 適合于樣式較多或者功能復(fù)雜的情況 // 3. 如果想要保留原先的類名,我們可以這么做 多類名選擇器 // this.className = 'change'; this.className = 'first change'; } </script></body>
4.4、總結(jié)
4.5、排他思想
如果有同一組元素,我們相要某一個元素實現(xiàn)某種樣式,需要用到循環(huán)的排他思想算法:
- 所有元素全部清除樣式(干掉其他人)
- 給當前元素設(shè)置樣式 (留下我自己)
- 注意順序不能顛倒,首先干掉其他人,再設(shè)置自己
<body> <button>按鈕1</button> <button>按鈕2</button> <button>按鈕3</button> <button>按鈕4</button> <button>按鈕5</button> <script> // 1. 獲取所有按鈕元素 var btns = document.getElementsByTagName('button'); // btns得到的是偽數(shù)組 里面的每一個元素 btns[i] for (var i = 0; i < btns.length; i++) { btns[i].onclick = function() { // (1) 我們先把所有的按鈕背景顏色去掉 干掉所有人 for (var i = 0; i < btns.length; i++) { btns[i].style.backgroundColor = ''; } // (2) 然后才讓當前的元素背景顏色為pink 留下我自己 this.style.backgroundColor = 'pink'; } } //2. 首先先排除其他人,然后才設(shè)置自己的樣式 這種排除其他人的思想我們成為排他思想 </script></body>
4.6、自定義屬性
4.6.1、獲取屬性值
- 獲取內(nèi)置屬性值(元素本身自帶的屬性)
element.屬性;
- 獲取自定義的屬性
element.getAttribute('屬性');
4.6.2、設(shè)置屬性值
- 設(shè)置內(nèi)置屬性值
element.屬性 = '值';
- 主要設(shè)置自定義的屬性
element.setAttribute('屬性');
4.6.3、移除屬性
element.removeAttribute('屬性');
<body> <p id="demo" index="1" class="nav"></p> <script> var p = document.querySelector('p'); // 1. 獲取元素的屬性值 // (1) element.屬性 console.log(p.id); //(2) element.getAttribute('屬性') get得到獲取 attribute 屬性的意思 我們程序員自己添加的屬性我們稱為自定義屬性 index console.log(p.getAttribute('id')); console.log(p.getAttribute('index')); // 2. 設(shè)置元素屬性值 // (1) element.屬性= '值' p.id = 'test'; p.className = 'navs'; // (2) element.setAttribute('屬性', '值'); 主要針對于自定義屬性 p.setAttribute('index', 2); p.setAttribute('class', 'footer'); // class 特殊 這里面寫的就是class 不是className // 3 移除屬性 removeAttribute(屬性) p.removeAttribute('index'); </script></body>
4.7、H5自定義屬性
自定義屬性目的:
- 保存并保存數(shù)據(jù),有些數(shù)據(jù)可以保存到頁面中而不用保存到數(shù)據(jù)庫中
- 有些自定義屬性很容易引起歧義,不容易判斷到底是內(nèi)置屬性還是自定義的,所以H5有了規(guī)定
2.8.1 設(shè)置H5自定義屬性
H5規(guī)定自定義屬性 data-
開頭作為屬性名并賦值
<p data-index = "1"></>// 或者使用JavaScript設(shè)置p.setAttribute('data-index',1);
2.8.2 獲取H5自定義屬性
- 兼容性獲取
element.getAttribute('data-index')
- H5新增的:
element.dataset.index
或element.dataset['index']
IE11才開始支持
<body> <p getTime="20" data-index="2" data-list-name="andy"></p> <script> var p = document.querySelector('p'); console.log(p.getAttribute('getTime')); p.setAttribute('data-time', 20); console.log(p.getAttribute('data-index')); console.log(p.getAttribute('data-list-name')); // h5新增的獲取自定義屬性的方法 它只能獲取data-開頭的 // dataset 是一個集合里面存放了所有以data開頭的自定義屬性 console.log(p.dataset); console.log(p.dataset.index); console.log(p.dataset['index']); // 如果自定義屬性里面有多個-鏈接的單詞,我們獲取的時候采取 駝峰命名法 console.log(p.dataset.listName); console.log(p.dataset['listName']); </script></body>
5、節(jié)點操作
獲取元素通常使用兩種方式:
1.利用DOM提供的方法獲取元素 | 2.利用節(jié)點層級關(guān)系獲取元素 |
---|---|
document.getElementById() | 利用父子兄節(jié)點關(guān)系獲取元素 |
document.getElementsByTagName() | 邏輯性強,但是兼容性較差 |
document.querySelector 等 | |
邏輯性不強,繁瑣 |
這兩種方式都可以獲取元素節(jié)點,我們后面都會使用,但是節(jié)點操作更簡單
一般的,節(jié)點至少擁有三個基本屬性
5.1、節(jié)點概述
網(wǎng)頁中的所有內(nèi)容都是節(jié)點(標簽、屬性、文本、注釋等),在DOM 中,節(jié)點使用 node 來表示。
HTML DOM 樹中的所有節(jié)點均可通過 JavaScript 進行訪問,所有 HTML 元素(節(jié)點)均可被修改,也可以創(chuàng)建或刪除。
一般的,節(jié)點至少擁有nodeType(節(jié)點類型)、nodeName(節(jié)點名稱)和nodeValue(節(jié)點值)這三個基本屬性。
- 元素節(jié)點:nodeType 為1
- 屬性節(jié)點:nodeType 為2
- 文本節(jié)點:nodeType 為3(文本節(jié)點包括文字、空格、換行等)
我們在實際開發(fā)中,節(jié)點操作主要操作的是元素節(jié)點
利用 DOM 樹可以把節(jié)點劃分為不同的層級關(guān)系,常見的是父子兄層級關(guān)系。
5.2、父級節(jié)點
node.parentNode
parentNode
屬性可以返回某節(jié)點的父結(jié)點,注意是最近的一個父結(jié)點- 如果指定的節(jié)點沒有父結(jié)點則返回null
<body> <!-- 節(jié)點的優(yōu)點 --> <p>我是p</p> <span>我是span</span> <ul> <li>我是li</li> <li>我是li</li> <li>我是li</li> <li>我是li</li> </ul> <p class="demo"> <p class="box"> <span class="erweima">×</span> </p> </p> <script> // 1. 父節(jié)點 parentNode var erweima = document.querySelector('.erweima'); // var box = document.querySelector('.box'); // 得到的是離元素最近的父級節(jié)點(親爸爸) 如果找不到父節(jié)點就返回為 null console.log(erweima.parentNode); </script></body>
5.3、子結(jié)點
parentNode.childNodes(標準)
parentNode.childNodes
返回包含指定節(jié)點的子節(jié)點的集合,該集合為即時更新的集合- 返回值包含了所有的子結(jié)點,包括元素節(jié)點,文本節(jié)點等
- 如果只想要獲得里面的元素節(jié)點,則需要專門處理。所以我們一般不提倡使用
childNodes
parentNode.children(非標準)
parentNode.children
是一個只讀屬性,返回所有的子元素節(jié)點- 它只返回子元素節(jié)點,其余節(jié)點不返回 (這個是我們重點掌握的)
- 雖然children是一個非標準,但是得到了各個瀏覽器的支持,因此我們可以放心使用
<body> <ul> <li>我是li</li> <li>我是li</li> <li>我是li</li> <li>我是li</li> </ul> <ol> <li>我是li</li> <li>我是li</li> <li>我是li</li> <li>我是li</li> </ol> <script> // DOM 提供的方法(API)獲取 var ul = document.querySelector('ul'); var lis = ul.querySelectorAll('li'); // 1. 子節(jié)點 childNodes 所有的子節(jié)點 包含 元素節(jié)點 文本節(jié)點等等 console.log(ul.childNodes); console.log(ul.childNodes[0].nodeType); console.log(ul.childNodes[1].nodeType); // 2. children 獲取所有的子元素節(jié)點 也是我們實際開發(fā)常用的 console.log(ul.children); </script></body>
5.3.1、第一個子結(jié)點
parentNode.firstChild
firstChild
返回第一個子節(jié)點,找不到則返回null- 同樣,也是包含所有的節(jié)點
5.3.2、最后一個子結(jié)點
parentNode.lastChild
lastChild
返回最后一個子節(jié)點,找不到則返回null- 同樣,也是包含所有的節(jié)點
<body> <ol> <li>我是li1</li> <li>我是li2</li> <li>我是li3</li> <li>我是li4</li> <li>我是li5</li> </ol> <script> var ol = document.querySelector('ol'); // 1. firstChild 第一個子節(jié)點 不管是文本節(jié)點還是元素節(jié)點 console.log(ol.firstChild); console.log(ol.lastChild); // 2. firstElementChild 返回第一個子元素節(jié)點 ie9才支持 console.log(ol.firstElementChild); console.log(ol.lastElementChild); // 3. 實際開發(fā)的寫法 既沒有兼容性問題又返回第一個子元素 console.log(ol.children[0]); //第一個子元素節(jié)點 console.log(ol.children[ol.children.length - 1]);//最后一個子元素節(jié)點 </script></body>
5.3.3、第一個子結(jié)點(兼容性)
parentNode.firstElementChild
firstElementChild
返回第一個子節(jié)點,找不到則返回null- 有兼容性問題,IE9以上才支持
5.3.4、最后一個子結(jié)點(兼容性)
parentNode.lastElementChild
lastElementChild
返回最后一個子節(jié)點,找不到則返回null- 有兼容性問題,IE9以上才支持
5.3.5、解決方案
實際開發(fā)中,firstChild 和 lastChild 包含其他節(jié)點,操作不方便,而 firstElementChild 和 lastElementChild 又有兼容性問題,那么我們?nèi)绾潍@取第一個子元素節(jié)點或最后一個子元素節(jié)點呢?
解決方案
-
如果想要第一個子元素節(jié)點,可以使用
parentNode.chilren[0]
-
如果想要最后一個子元素節(jié)點,可以使用
// 數(shù)組元素個數(shù)減1 就是最后一個元素的索引號parentNode.chilren[parentNode.chilren.length - 1]
-
示例:
<body> <ol> <li>我是li1</li> <li>我是li2</li> <li>我是li3</li> <li>我是li4</li> </ol> <script> var ol = document.querySelector('ol'); // 1.firstChild 獲取第一個子結(jié)點的,包含文本結(jié)點和元素結(jié)點 console.log(ol.firstChild); // 返回的是文本結(jié)點 #text(第一個換行結(jié)點) console.log(ol.lastChild); // 返回的是文本結(jié)點 #text(最后一個換行結(jié)點) // 2. firstElementChild 返回第一個子元素結(jié)點 console.log(ol.firstElementChild); // <li>我是li1</li> // 第2個方法有兼容性問題,需要IE9以上才支持 // 3.實際開發(fā)中,既沒有兼容性問題,又返回第一個子元素 console.log(ol.children[0]); // <li>我是li1</li> console.log(ol.children[3]); // <li>我是li4</li> // 當里面li個數(shù)不唯一時候,需要取到最后一個結(jié)點時這么寫 console.log(ol.children[ol.children.length - 1]); </script></body>
5.4、兄弟節(jié)點
5.4.1、下一個兄弟節(jié)點
node.nextSibling
nextSibling
返回當前元素的下一個兄弟元素節(jié)點,找不到則返回null- 同樣,也是包含所有的節(jié)點
5.4.2、上一個兄弟節(jié)點
node.previousSibling
-
previousSibling
返回當前元素上一個兄弟元素節(jié)點,找不到則返回null -
同樣,也是包含所有的節(jié)點
5.4.3、下一個兄弟節(jié)點(兼容性)
node.nextElementSibling
nextElementSibling
返回當前元素下一個兄弟元素節(jié)點,找不到則返回null- 有兼容性問題,IE9才支持
5.4.4、上一個兄弟節(jié)點(兼容性)
node.previousElementSibling
previousElementSibling
返回當前元素上一個兄弟元素節(jié)點,找不到則返回null- 有兼容性問題,IE9才支持
示例
<body> <p>我是p</p> <span>我是span</span> <script> var p = document.querySelector('p'); // 1.nextSibling 下一個兄弟節(jié)點 包含元素節(jié)點或者 文本節(jié)點等等 console.log(p.nextSibling); // #text console.log(p.previousSibling); // #text // 2. nextElementSibling 得到下一個兄弟元素節(jié)點 console.log(p.nextElementSibling); //<span>我是span</span> console.log(p.previousElementSibling);//null </script></body>
如何解決兼容性問題 ?
答:自己封裝一個兼容性的函數(shù)
function getNextElementSibling(element) { var el = element; while(el = el.nextSibling) { if(el.nodeType === 1){ return el; } } return null;}
5.5、創(chuàng)建節(jié)點
document.createElement('tagName');
document.createElement()
方法創(chuàng)建由 tagName 指定的HTML 元素- 因為這些元素原先不存在,是根據(jù)我們的需求動態(tài)生成的,所以我們也稱為動態(tài)創(chuàng)建元素節(jié)點
5.5.1、添加節(jié)點
node.appendChild(child)
node.appendChild()
方法將一個節(jié)點添加到指定父節(jié)點的子節(jié)點列表末尾。類似于 CSS 里面的 after 偽元素。
node.insertBefore(child,指定元素)
node.insertBefore()
方法將一個節(jié)點添加到父節(jié)點的指定子節(jié)點前面。類似于 CSS 里面的 before 偽元素。
示例
<body> <ul> <li>123</li> </ul> <script> // 1. 創(chuàng)建節(jié)點元素節(jié)點 var li = document.createElement('li'); // 2. 添加節(jié)點 node.appendChild(child) node 父級 child 是子級 后面追加元素 類似于數(shù)組中的push // 先獲取父親ul var ul = document.querySelector('ul'); ul.appendChild(li); // 3. 添加節(jié)點 node.insertBefore(child, 指定元素); var lili = document.createElement('li'); ul.insertBefore(lili, ul.children[0]); // 4. 我們想要頁面添加一個新的元素分兩步: 1. 創(chuàng)建元素 2. 添加元素 </script></body>
5.5.2、刪除節(jié)點
node.removeChild(child)
node.removeChild()
方法從 DOM 中刪除一個子節(jié)點,返回刪除的節(jié)點
5.5.3、復(fù)制節(jié)點(克隆節(jié)點)
node.cloneNode()
node.cloneNode()
方法返回調(diào)用該方法的節(jié)點的一個副本。 也稱為克隆節(jié)點/拷貝節(jié)點- 如果括號參數(shù)為空或者為 false ,則是淺拷貝,即只克隆復(fù)制節(jié)點本身,不克隆里面的子節(jié)點
- 如果括號參數(shù)為 true ,則是深度拷貝,會復(fù)制節(jié)點本身以及里面所有的子節(jié)點
示例
<body> <ul> <li>1111</li> <li>2</li> <li>3</li> </ul> <script> var ul = document.querySelector('ul'); // 1. node.cloneNode(); 括號為空或者里面是false 淺拷貝 只復(fù)制標簽不復(fù)制里面的內(nèi)容 // 2. node.cloneNode(true); 括號為true 深拷貝 復(fù)制標簽復(fù)制里面的內(nèi)容 var lili = ul.children[0].cloneNode(true); ul.appendChild(lili); </script></body>
5.5.4、面試題
三種動態(tài)創(chuàng)建元素的區(qū)別
- doucument.write()
- element.innerHTML
- document.createElement()
區(qū)別:
document.write()
是直接將內(nèi)容寫入頁面的內(nèi)容流,但是文檔流執(zhí)行完畢,則它會導(dǎo)致頁面全部重繪innerHTML
是將內(nèi)容寫入某個 DOM 節(jié)點,不會導(dǎo)致頁面全部重繪innerHTML
創(chuàng)建多個元素效率更高(不要拼接字符串,采取數(shù)組形式拼接),結(jié)構(gòu)稍微復(fù)雜
<body> <p class="innner"></p> <p class="create"></p> <script> // 2. innerHTML 創(chuàng)建元素 var inner = document.querySelector('.inner'); // 2.1 innerHTML 用拼接字符串方法 for (var i = 0; i <= 100; i++) { inner.innerHTML += '<a href="#">百度</a>'; } // 2.2 innerHTML 用數(shù)組形式拼接 var arr = []; for (var i = 0; i <= 100; i++) { arr.push('<a href="#">百度</a>'); } inner.innerHTML = arr.join(''); // 3.document.createElement() 創(chuàng)建元素 var create = document.querySelector('.create'); var a = document.createElement('a'); create.appendChild(a); </script></body>
createElement()
創(chuàng)建多個元素效率稍低一點點,但是結(jié)構(gòu)更清晰
總結(jié):不同瀏覽器下, innerHTML 效率要比 createElement 高
6、DOM核心
對于DOM操作,我們主要針對子元素的操作,主要有
- 創(chuàng)建
- 增
- 刪
- 改
- 查
- 屬性操作
- 時間操作
6.1、創(chuàng)建
- document.write
- innerHTML
- createElement
6.2、增
- appendChild
- insertBefore
6.3、刪
- removeChild
6.4、改
- 主要修改dom的元素屬性,dom元素的內(nèi)容、屬性、表單的值等
- 修改元素屬性:src、href、title 等
- 修改普通元素內(nèi)容:innerHTML、innerText
- 修改表單元素:value、type、disabled
- 修改元素樣式:style、className
6.5、查
- 主要獲取查詢dom的元素
- DOM提供的API方法:getElementById、getElementsByTagName (古老用法,不推薦)
- H5提供的新方法:querySelector、querySelectorAll (提倡)
- 利用節(jié)點操作獲取元素:父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling) 提倡
6.6、屬性操作
- 主要針對于自定義屬性
- setAttribute:設(shè)置dom的屬性值
- getAttribute:得到dom的屬性值
- removeAttribute:移除屬性
7、事件高級
7.1、注冊事件(綁定事件)
給元素添加事件,稱為注冊事件或者綁定事件。
注冊事件有兩種方式:傳統(tǒng)方式和方法監(jiān)聽注冊方式
傳統(tǒng)注冊方式 | 方法監(jiān)聽注冊方式 |
---|---|
利用 on 開頭的事件 onclick | w3c 標準推薦方式 |
<button onclick = "alert("hi")"></button> |
addEventListener() 它是一個方法 |
btn.onclick = function() {} | IE9 之前的 IE 不支持此方法,可使用 attachEvent() 代替 |
特點:注冊事件的唯一性 | 特點:同一個元素同一個事件可以注冊多個監(jiān)聽器 |
同一個元素同一個事件只能設(shè)置一個處理函數(shù),最后注冊的處理函數(shù)將會覆蓋前面注冊的處理函數(shù) | 按注冊順序依次執(zhí)行 |
addEventListener事件監(jiān)聽方式
eventTarget.addEventListener()
方法將指定的監(jiān)聽器注冊到 eventTarget(目標對象)上- 當該對象觸發(fā)指定的事件時,就會執(zhí)行事件處理函數(shù)
eventTarget.addEventListener(type,listener[,useCapture])
該方法接收三個參數(shù):
type
:事件類型字符串,比如click,mouseover,注意這里不要帶onlistener
:事件處理函數(shù),事件發(fā)生時,會調(diào)用該監(jiān)聽函數(shù)useCapture
:可選參數(shù),是一個布爾值,默認是 false。學(xué)完 DOM 事件流后,我們再進一步學(xué)習
<body> <button>傳統(tǒng)注冊事件</button> <button>方法監(jiān)聽注冊事件</button> <button>ie9 attachEvent</button> <script> var btns = document.querySelectorAll('button'); // 1. 傳統(tǒng)方式注冊事件 btns[0].onclick = function() { alert('hi'); } btns[0].onclick = function() { alert('hao a u'); } // 2. 事件監(jiān)聽注冊事件 addEventListener // (1) 里面的事件類型是字符串 所以加引號 而且不帶on // (2) 同一個元素 同一個事件可以添加多個偵聽器(事件處理程序) btns[1].addEventListener('click', function() { alert(22); }) btns[1].addEventListener('click', function() { alert(33); }) // 3. attachEvent ie9以前的版本支持 btns[2].attachEvent('onclick', function() { alert(11); }) </script></body>
②attachEvent事件監(jiān)聽方式(兼容)
eventTarget.attachEvent()
方法將指定的監(jiān)聽器注冊到 eventTarget(目標對象) 上- 當該對象觸發(fā)指定的事件時,指定的回調(diào)函數(shù)就會被執(zhí)行
eventTarget.attachEvent(eventNameWithOn,callback)
該方法接收兩個參數(shù):
eventNameWithOn
:事件類型字符串,比如 onclick 、onmouseover ,這里要帶 oncallback
: 事件處理函數(shù),當目標觸發(fā)事件時回調(diào)函數(shù)被調(diào)用- ie9以前的版本支持
③注冊事件兼容性解決方案
兼容性處理的原則:首先照顧大多數(shù)瀏覽器,再處理特殊瀏覽器
function addEventListener(element, eventName, fn) { // 判斷當前瀏覽器是否支持 addEventListener 方法 if (element.addEventListener) { element.addEventListener(eventName, fn); // 第三個參數(shù) 默認是false } else if (element.attachEvent) { element.attachEvent('on' + eventName, fn); } else { // 相當于 element.onclick = fn; element['on' + eventName] = fn; }
7.2、刪除事件(解綁事件)
7.2.1、removeEventListener刪除事件方式
eventTarget.removeEventListener(type,listener[,useCapture]);
該方法接收三個參數(shù):
type
:事件類型字符串,比如click,mouseover,注意這里不要帶onlistener
:事件處理函數(shù),事件發(fā)生時,會調(diào)用該監(jiān)聽函數(shù)useCapture
:可選參數(shù),是一個布爾值,默認是 false。學(xué)完 DOM 事件流后,我們再進一步學(xué)習
7.2.2、detachEvent刪除事件方式(兼容)
eventTarget.detachEvent(eventNameWithOn,callback);
該方法接收兩個參數(shù):
eventNameWithOn
:事件類型字符串,比如 onclick 、onmouseover ,這里要帶 oncallback
: 事件處理函數(shù),當目標觸發(fā)事件時回調(diào)函數(shù)被調(diào)用- ie9以前的版本支持
7.2.3、傳統(tǒng)事件刪除方式
eventTarget.onclick = null;
事件刪除示例:
<body> <p>1</p> <p>2</p> <p>3</p> <script> var ps = document.querySelectorAll('p'); ps[0].onclick = function() { alert(11); // 1. 傳統(tǒng)方式刪除事件 ps[0].onclick = null; } // 2.removeEventListener 刪除事件 ps[1].addEventListener('click',fn); //里面的fn不需要調(diào)用加小括號 function fn(){ alert(22); ps[1].removeEventListener('click',fn); } // 3.IE9 中的刪除事件方式 ps[2].attachEvent('onclick',fn1); function fn1() { alert(33); ps[2].detachEvent('onclick',fn1); } </script></body>
7.2.4、刪除事件兼容性解決方案
function removeEventListener(element, eventName, fn) { // 判斷當前瀏覽器是否支持 removeEventListener 方法 if (element.removeEventListener) { element.removeEventListener(eventName, fn); // 第三個參數(shù) 默認是false } else if (element.detachEvent) { element.detachEvent('on' + eventName, fn); } else { element['on' + eventName] = null; }
7.3、DOM事件流
- 事件流描述的是從頁面中接收事件的順序
- 事件發(fā)生時會在元素節(jié)點之間按照特定的順序傳播,這個傳播過程即DOM事件流
-
事件冒泡: IE 最早提出,事件開始時由最具體的元素接收,然后逐級向上傳播到到 DOM 最頂層節(jié)點的過程。
-
事件捕獲: 網(wǎng)景最早提出,由 DOM 最頂層節(jié)點開始,然后逐級向下傳播到到最具體的元素接收的過程。
加深理解:
我們向水里面扔一塊石頭,首先它會有一個下降的過程,這個過程就可以理解為從最頂層向事件發(fā)生的最具體元素(目標點)的捕獲過程;之后會產(chǎn)生泡泡,會在最低點( 最具體元素)之后漂浮到水面上,這個過程相當于事件冒泡。
7.3.1、捕獲階段
- document -> html -> body -> father -> son
兩個盒子嵌套,一個父盒子一個子盒子,我們的需求是當點擊父盒子時彈出 father ,當點擊子盒子時彈出 son
<body> <p class="father"> <p class="son">son盒子</p> </p> <script> // dom 事件流 三個階段 // 1. JS 代碼中只能執(zhí)行捕獲或者冒泡其中的一個階段。 // 2. onclick 和 attachEvent(ie) 只能得到冒泡階段。 // 3. 捕獲階段 如果addEventListener 第三個參數(shù)是 true 那么則處于捕獲階段 document -> html -> body -> father -> son var son = document.querySelector('.son'); son.addEventListener('click', function() { alert('son'); }, true); var father = document.querySelector('.father'); father.addEventListener('click', function() { alert('father'); }, true); </script></body>
但是因為DOM流的影響,我們點擊子盒子,會先彈出 father,之后再彈出 son
這是因為捕獲階段由 DOM 最頂層節(jié)點開始,然后逐級向下傳播到到最具體的元素接收
-
document -> html -> body -> father -> son
-
先看 document 的事件,沒有;再看 html 的事件,沒有;再看 body 的事件,沒有;再看 father 的事件,有就先執(zhí)行;再看 son 的事件,再執(zhí)行。
7.3.2、冒泡階段
- son -> father ->body -> html -> document
<body> <p class="father"> <p class="son">son盒子</p> </p> <script> // 4. 冒泡階段 如果addEventListener 第三個參數(shù)是 false 或者 省略 那么則處于冒泡階段 son -> father ->body -> html -> document var son = document.querySelector('.son'); son.addEventListener('click', function() { alert('son'); }, false); var father = document.querySelector('.father'); father.addEventListener('click', function() { alert('father'); }, false); document.addEventListener('click', function() { alert('document'); }) </script></body>
我們點擊子盒子,會彈出 son、father、document
這是因為冒泡階段開始時由最具體的元素接收,然后逐級向上傳播到到 DOM 最頂層節(jié)點
- son -> father ->body -> html -> document
7.3.3、小結(jié)
-
JS 代碼中只能執(zhí)行捕獲或者冒泡其中的一個階段
-
onclick
和attachEvent
只能得到冒泡階段 -
addEventListener(type,listener[,useCapture])
第三個參數(shù)如果是 true,表示在事件捕獲階段調(diào)用事件處理程序;如果是 false (不寫默認就是false),表示在事件冒泡階段調(diào)用事件處理程序 -
實際開發(fā)中我們很少使用事件捕獲,我們更關(guān)注事件冒泡。
-
有些事件是沒有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave
7.4、事件對象
eventTarget.onclick = function(event) { // 這個 event 就是事件對象,我們還喜歡的寫成 e 或者 evt } eventTarget.addEventListener('click', function(event) { // 這個 event 就是事件對象,我們還喜歡的寫成 e 或者 evt })
- 官方解釋:event 對象代表事件的狀態(tài),比如鍵盤按鍵的狀態(tài)、鼠標的位置、鼠標按鈕的狀態(tài)
- 簡單理解:
- 事件發(fā)生后,跟事件相關(guān)的一系列信息數(shù)據(jù)的集合都放到這個對象里面
- 這個對象就是事件對象 event,它有很多屬性和方法,比如“
- 誰綁定了這個事件
- 鼠標觸發(fā)事件的話,會得到鼠標的相關(guān)信息,如鼠標位置
- 鍵盤觸發(fā)事件的話,會得到鍵盤的相關(guān)信息,如按了哪個鍵
- 這個 event 是個形參,系統(tǒng)幫我們設(shè)定為事件對象,不需要傳遞實參過去
- 當我們注冊事件時, event 對象就會被系統(tǒng)自動創(chuàng)建,并依次傳遞給事件監(jiān)聽器(事件處理函數(shù))
<body> <p>123</p> <script> // 事件對象 var p = document.querySelector('p'); p.onclick = function(e) { // console.log(e); // console.log(window.event); // e = e || window.event; console.log(e); } // 1. event 就是一個事件對象 寫到我們偵聽函數(shù)的 小括號里面 當形參來看 // 2. 事件對象只有有了事件才會存在,它是系統(tǒng)給我們自動創(chuàng)建的,不需要我們傳遞參數(shù) // 3. 事件對象 是 我們事件的一系列相關(guān)數(shù)據(jù)的集合 跟事件相關(guān)的 比如鼠標點擊里面就包含了鼠標的相關(guān)信息,鼠標坐標啊,如果是鍵盤事件里面就包含的鍵盤事件的信息 比如 判斷用戶按下了那個鍵 // 4. 這個事件對象我們可以自己命名 比如 event 、 evt、 e // 5. 事件對象也有兼容性問題 ie678 通過 window.event 兼容性的寫法 e = e || window.event; </script></body>
7.4.1、事件對象的兼容性方案
事件對象本身的獲取存在兼容問題:
- 標準瀏覽器中是瀏覽器給方法傳遞的參數(shù),只需要定義形參 e 就可以獲取到。
- 在 IE6~8 中,瀏覽器不會給方法傳遞參數(shù),如果需要的話,需要到 window.event 中獲取查找
解決:
e = e || window.event;
7.4.2、事件對象的常見屬性和方法
事件對象屬性方法 | 說明 |
---|---|
e.target | 返回觸發(fā)事件的對象 標準 |
e.srcElement | 返回觸發(fā)事件的對象 非標準 ie6-8使用 |
e.type | 返回事件的類型 比如click mouseover 不帶on |
e.cancelBubble | 該屬性阻止冒泡,非標準,ie6-8使用 |
e.returnValue | 該屬性阻止默認行為 非標準,ie6-8使用 |
e.preventDefault() | 該方法阻止默認行為 標準 比如不讓鏈接跳轉(zhuǎn) |
e.stopPropagation() | 阻止冒泡 標準 |
e.target和this的區(qū)別:
- this 是事件綁定的元素, 這個函數(shù)的調(diào)用者(綁定這個事件的元素)
- e.target 是事件觸發(fā)的元素。
7.5、事件對象阻止默認行為
<body> <p>123</p> <a href="http://www.baidu.com">百度</a> <form action="http://www.baidu.com"> <input type="submit" value="提交" name="sub"> </form> <script> // 常見事件對象的屬性和方法 // 1. 返回事件類型 var p = document.querySelector('p'); p.addEventListener('click', fn); p.addEventListener('mouseover', fn); p.addEventListener('mouseout', fn); function fn(e) { console.log(e.type); } // 2. 阻止默認行為(事件) 讓鏈接不跳轉(zhuǎn) 或者讓提交按鈕不提交 var a = document.querySelector('a'); a.addEventListener('click', function(e) { e.preventDefault(); // dom 標準寫法 }) // 3. 傳統(tǒng)的注冊方式 a.onclick = function(e) { // 普通瀏覽器 e.preventDefault(); 方法 // e.preventDefault(); // 低版本瀏覽器 ie678 returnValue 屬性 // e.returnValue; // 我們可以利用return false 也能阻止默認行為 沒有兼容性問題 特點: return 后面的代碼不執(zhí)行了, 而且只限于傳統(tǒng)的注冊方式 return false; alert(11); } </script></body>
7.6、阻止事件冒泡
事件冒泡:開始時由最具體的元素接收,然后逐級向上傳播到到 DOM 最頂層節(jié)點
事件冒泡本身的特性,會帶來的壞處,也會帶來的好處,需要我們靈活掌握。
- 標準寫法
e.stopPropagation();
- 非標準寫法: IE6-8 利用對象事件 cancelBubble屬性
e.cancelBubble = true;
<body> <p class="father"> <p class="son">son兒子</p> </p> <script> // 常見事件對象的屬性和方法 // 阻止冒泡 dom 推薦的標準 stopPropagation() var son = document.querySelector('.son'); son.addEventListener('click', function(e) { alert('son'); e.stopPropagation(); // stop 停止 Propagation 傳播 e.cancelBubble = true; // 非標準 cancel 取消 bubble 泡泡 }, false); var father = document.querySelector('.father'); father.addEventListener('click', function() { alert('father'); }, false); document.addEventListener('click', function() { alert('document'); }) </script></body>
7.6.1、阻止事件冒泡的兼容性解決方案
if(e && e.stopPropagation){ e.stopPropagation(); }else{ window.event.cancelBubble = true; }
4.4.4 e.target 與 this
e.target 與 this 的區(qū)別
this
是事件綁定的元素,這個函數(shù)的調(diào)用者(綁定這個事件的元素)e.target
是事件觸發(fā)的元素
<body> <p>123</p> <ul> <li>abc</li> <li>abc</li> <li>abc</li> </ul> <script> // 常見事件對象的屬性和方法 // 1. e.target 返回的是觸發(fā)事件的對象(元素) this 返回的是綁定事件的對象(元素) // 區(qū)別 : e.target 點擊了那個元素,就返回那個元素 this 那個元素綁定了這個點擊事件,那么就返回誰 var p = document.querySelector('p'); p.addEventListener('click', function(e) { console.log(e.target); console.log(this); }) var ul = document.querySelector('ul'); ul.addEventListener('click', function(e) { // 我們給ul 綁定了事件 那么this 就指向ul console.log(this); console.log(e.currentTarget); // e.target 指向我們點擊的那個對象 誰觸發(fā)了這個事件 我們點擊的是li e.target 指向的就是li console.log(e.target); }) // 了解兼容性 // p.onclick = function(e) { // e = e || window.event; // var target = e.target || e.srcElement; // console.log(target); // } // 2. 了解 跟 this 有個非常相似的屬性 currentTarget ie678不認識 </script></body>
4.4.5 事件對象的兼容性
事件對象本身的獲取存在兼容問題:
- 標準瀏覽器中瀏覽器是給方法傳遞的參數(shù),只需定義形參e就可以獲取到
- 在 IE6 -> 8 中,瀏覽器不會給方法傳遞參數(shù),如果需要的話,需要到
window.event
中獲取查找
解決方案
e = e || window.event
<body> <p>123</p> <script> // 事件對象 var p = document.querySelector('p'); p.onclick = function(e) { // e = e || window.event; console.log(e); // 事件對象也有兼容性問題 ie678 通過 window.event 兼容性的寫法 e = e || window.event; }</body>
7.7、事件委托
- 事件委托也稱為事件代理,在 jQuery 里面稱為事件委派
- 事件委托的原理
- 不是每個子節(jié)點單獨設(shè)置事件監(jiān)聽器,而是事件監(jiān)聽器設(shè)置在其父節(jié)點上,然后利用冒泡原理影響設(shè)置每個子節(jié)點
<body> <ul> <li>知否知否,點我應(yīng)有彈框在手!</li> <li>知否知否,點我應(yīng)有彈框在手!</li> <li>知否知否,點我應(yīng)有彈框在手!</li> <li>知否知否,點我應(yīng)有彈框在手!</li> <li>知否知否,點我應(yīng)有彈框在手!</li> </ul> <script> // 事件委托的核心原理:給父節(jié)點添加偵聽器, 利用事件冒泡影響每一個子節(jié)點 var ul = document.querySelector('ul'); ul.addEventListener('click', function(e) { // alert('知否知否,點我應(yīng)有彈框在手!'); // e.target 這個可以得到我們點擊的對象 e.target.style.backgroundColor = 'pink'; // 點了誰,就讓誰的style里面的backgroundColor顏色變?yōu)閜ink }) </script></body>
以上案例:給 ul 注冊點擊事件,然后利用事件對象的 target 來找到當前點擊的 li,因為點擊 li,事件會冒泡到 ul 上, ul 有注冊事件,就會觸發(fā)事件監(jiān)聽器。
7.8、常見的鼠標事件
鼠標事件 | 觸發(fā)條件 |
---|---|
onclick | 鼠標點擊左鍵觸發(fā) |
onmouseover | 鼠標經(jīng)過觸發(fā) |
onmouseout | 鼠標離開觸發(fā) |
onfocus | 獲得鼠標焦點觸發(fā) |
onblur | 失去鼠標焦點觸發(fā) |
onmousemove | 鼠標移動觸發(fā) |
onmouseup | 鼠標彈起觸發(fā) |
onmousedown | 鼠標按下觸發(fā) |
7.8.1、禁止鼠標右鍵與鼠標選中
contextmenu
主要控制應(yīng)該何時顯示上下文菜單,主要用于程序員取消默認的上下文菜單selectstart
禁止鼠標選中
<body> <h1>我是一段不愿意分享的文字</h1> <script> // 1. contextmenu 我們可以禁用右鍵菜單 document.addEventListener('contextmenu', function(e) { e.preventDefault(); // 阻止默認行為 }) // 2. 禁止選中文字 selectstart document.addEventListener('selectstart', function(e) { e.preventDefault(); }) </script></body>
7.8.2、鼠標事件對象
- event對象代表事件的狀態(tài),跟事件相關(guān)的一系列信息的集合
- 現(xiàn)階段我們主要是用鼠標事件對象 MouseEvent 和鍵盤事件對象 KeyboardEvent。
鼠標事件對象 | 說明 |
---|---|
e.clientX | 返回鼠標相對于瀏覽器窗口可視區(qū)的X坐標 |
e.clientY | 返回鼠標相對于瀏覽器窗口可視區(qū)的Y坐標 |
e.pageX(重點) | 返回鼠標相對于文檔頁面的X坐標 IE9+ 支持 |
e.pageY(重點) | 返回鼠標相對于文檔頁面的Y坐標 IE9+ 支持 |
e.screenX | 返回鼠標相對于電腦屏幕的X坐標 |
e.screenY | 返回鼠標相對于電腦屏幕的Y坐標 |
<body> <script> // 鼠標事件對象 MouseEvent document.addEventListener('click', function(e) { // 1. client 鼠標在可視區(qū)的x和y坐標 console.log(e.clientX); console.log(e.clientY); console.log('---------------------'); // 2. page 鼠標在頁面文檔的x和y坐標 console.log(e.pageX); console.log(e.pageY); console.log('---------------------'); // 3. screen 鼠標在電腦屏幕的x和y坐標 console.log(e.screenX); console.log(e.screenY); }) </script></body>
7.9、常用的鍵盤事件
鍵盤事件 | 觸發(fā)條件 |
---|---|
onkeyup | 某個鍵盤按鍵被松開時觸發(fā) |
onkeydown | 某個鍵盤按鍵被按下時觸發(fā) |
onkeypress | 某個鍵盤按鍵被按下時觸發(fā),但是它不識別功能鍵,比如 ctrl shift 箭頭等 |
- 如果使用addEventListener 不需要加 on
onkeypress
和前面2個的區(qū)別是,它不識別功能鍵,比如左右箭頭,shift 等- 三個事件的執(zhí)行順序是: keydown – keypress — keyup
<body> <script> // 常用的鍵盤事件 //1. keyup 按鍵彈起的時候觸發(fā) // document.onkeyup = function() { // console.log('我彈起了'); // } document.addEventListener('keyup', function() { console.log('我彈起了'); }) //3. keypress 按鍵按下的時候觸發(fā) 不能識別功能鍵 比如 ctrl shift 左右箭頭啊 document.addEventListener('keypress', function() { console.log('我按下了press'); }) //2. keydown 按鍵按下的時候觸發(fā) 能識別功能鍵 比如 ctrl shift 左右箭頭啊 document.addEventListener('keydown', function() { console.log('我按下了down'); }) // 4. 三個事件的執(zhí)行順序 keydown -- keypress -- keyup </script></body>
7.9.1、鍵盤對象屬性
鍵盤事件對象 屬性 | 說明 |
---|---|
keyCode | 返回該鍵值的ASCII值 |
onkeydown
和onkeyup
不區(qū)分字母大小寫,onkeypress
區(qū)分字母大小寫。- 在我們實際開發(fā)中,我們