久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

JavaScript學(xué)習(xí)筆記:DOM的操作

 ipilipala 2018-09-28

通過上一節(jié)的學(xué)習(xí),對(duì)JavaScript中的DOM有了一定的認(rèn)識(shí),。雖然對(duì)DOM中相關(guān)的知識(shí)點(diǎn)有一定的概念,但還是缺乏對(duì)DOM的實(shí)際操作,。如果你仔細(xì)閱讀過上一篇文章的話,你應(yīng)該會(huì)發(fā)現(xiàn),當(dāng)時(shí)也提到了一些DOM操作相關(guān)的東西,,比如,,DOM的,、等,。那麼今天我們就來看看這些方面的東西,。

DOM的增

先來看DOM操作中的。其主要分為兩個(gè)部分:新創(chuàng)建節(jié)點(diǎn)插入節(jié)點(diǎn),。

新創(chuàng)建節(jié)點(diǎn)

常用的DOM節(jié)點(diǎn)創(chuàng)建有關(guān)的API接口主要有:

  • document.createElement:創(chuàng)建指定的HTML元素或一個(gè)HTMLUnknownElement
  • document.createTextNode:創(chuàng)建文本節(jié)點(diǎn)
  • document.createDocumentFrame:創(chuàng)建文檔片段
  • document.createAttribute:創(chuàng)建節(jié)點(diǎn)屬性
  • document.adoptNode:從外部文檔中獲取一個(gè)節(jié)點(diǎn)
  • document.importNode:拷貝外部文檔的一個(gè)節(jié)點(diǎn)
  • node.cloneNode:克隆節(jié)點(diǎn)

document.createElement

document.createElement(tagName[, options])是其中最常用的DOM API之一,,主要用來創(chuàng)建由標(biāo)籤名稱(tagName)指定的HTML元素,如果標(biāo)籤名稱不是一個(gè)有效的HTML元素,,將會(huì)創(chuàng)建一個(gè)HTMLUnknownElement對(duì)象,。來看一個(gè)簡(jiǎn)單的示例:

let newEle = document.createElement('div');
let newContent = document.createTextNode('我是一個(gè)新創(chuàng)建的div元素')
newEle.appendChild(newContent)
document.body.appendChild(newEle)

注意,通過document.createElement創(chuàng)建的元素並不屬於document對(duì)象,,它只是創(chuàng)建出來,,並未添加到HTML文檔中,需要調(diào)用appendChild()insertBefore()等方法將其添加到HTML文檔中,。

如果你對(duì)HTMLUnknownElement從未接觸,,建議你有空花點(diǎn)時(shí)間閱讀@張?chǎng)涡窭蠞竦摹?a href="http://www./wordpress/2018/03/htmlunknownelement-html5-custom-elements/" target="_blank">》和@米粽大大翻譯的《Custom Elements》。

document.createTextNode

document.createTextNode(text)創(chuàng)建一個(gè)文本節(jié)點(diǎn),,參數(shù)text為文本節(jié)點(diǎn)的內(nèi)容,。比如:

let newContent = document.createTextNode('我是一個(gè)新創(chuàng)建的div元素')
newEle.appendChild(newContent)

創(chuàng)建了一個(gè)文本節(jié)點(diǎn)newContent,然後把這個(gè)新創(chuàng)建的文本節(jié)點(diǎn)通過appendChild()方法,,將其插入到newEle元素中,,當(dāng)作其內(nèi)容。document.createTextNode(text)方法返回的節(jié)點(diǎn),被瀏覽器當(dāng)作文本渲染,,而不是當(dāng)作HTML代碼渲染,,因此會(huì)對(duì)HTML代碼進(jìn)行轉(zhuǎn)義,可以用來展示用戶的輸入,,避免XSS攻擊,。

function escapeUserInput(str) {
    var div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
}

var userInput = '<p>危險(xiǎn)內(nèi)容</p>';
var template = '<div>' + escapeUserInput(userInput) + '</div>'

// 此時(shí)被轉(zhuǎn)義,危險(xiǎn)內(nèi)容不再危險(xiǎn)
<div><p>危險(xiǎn)內(nèi)容</p></div>

但是,,該方法不對(duì)單引號(hào)和雙引號(hào)轉(zhuǎn)義,,因此用來為屬性賦值的時(shí)候,仍然會(huì)被 XSS 攻擊:

var userInput = '" onmouseover="console.log(\'危險(xiǎn)操作\')" "';
var template = '<div color="' + escapeUserInput(userInput) + '">user set color</div>'

// 被注入一個(gè) onmouseover 操作
<div color="" onmouseover="console.log('危險(xiǎn)操作')" "">user set color</div>

其中XSS攻擊屬於Web安全方面的知識(shí)了,,不屬於這篇文章的範(fàn)疇,。如果你對(duì)XSS相關(guān)的東西感興趣的話,可以看看下面幾篇文章:

document.createDocumentFragment

document.createDocumentFragment()方法創(chuàng)建一個(gè)新空白的DocumentFragment對(duì)象,。

DocumentFragments是DOM節(jié)點(diǎn),。它們不是主DOM樹的一部分。通常的用例是創(chuàng)建文檔片段,,將元素附加到文檔片段,,然後將文檔片段附加到DOM樹,。在DOM樹中,,文檔片段被其所有的子元素代替。

因?yàn)槲臋n片段存在於內(nèi)存中,,並不在DOM樹中,,所以將子元素插入到文檔片段時(shí)不會(huì)引起頁(yè)面回流(reflow)(對(duì)元素位置和幾何上的計(jì)算)。因此,,使用文檔片段document fragments 通常會(huì)起到優(yōu)化性能的作用,。

比如下面這個(gè)示例,給一個(gè)ul添加10000個(gè)li,,先用拼接字符串的方式來實(shí)現(xiàn):

let start = Date.now()
let str = ''
let newUlEle = document.createElement('ul')

document.body.appendChild(newUlEle)

for (let i = 0; i < 10000; i++) {
    str += '<li>第' + i + '個(gè)子節(jié)點(diǎn)</li>'
}

newUlEle.innerHTML = str

console.log('耗時(shí)' + (Date.now() - start) + 'ms');

多次刷新,,可以看到創(chuàng)建10000個(gè)li時(shí),渲染所需要的時(shí)間如下圖:

把上面的示例,,換成append()的方式,,逐個(gè)添加對(duì)應(yīng)的li

let start = Date.now()
let str = ''
let newUlEle = document.createElement('ul')

document.body.appendChild(newUlEle)

for (let i = 0; i < 10000; i++) {
    let liEle = document.createElement('li')

    liEle.textContent = '第' + i + '個(gè)子節(jié)點(diǎn)'
    newUlEle.appendChild(liEle)
}

console.log('耗時(shí):' + (Date.now() - start) + 'ms')

這種方法所費(fèi)時(shí)間如下圖:

都說第二種方法要比第一種方法耗時(shí),看上去有點(diǎn)像,。接下來再來看createDocumentFragment的方法,。可以預(yù)見的是,,這種方法肯定比第二種強(qiáng),,但應(yīng)該沒有第一種快:

let start = Date.now()
let str = ''
let newUlEle = document.createElement('ul')

document.body.appendChild(newUlEle)

let fragment = document.createDocumentFragment()

for (let i = 0; i < 10000; i++) {
    let liEle = document.createElement('li')

    liEle.textContent = '第' + i + '個(gè)子節(jié)點(diǎn)'
    fragment.appendChild(liEle)
}

newUlEle.appendChild(fragment)

console.log('耗時(shí):' + (Date.now() - start) + 'ms')

document.createAttribute()

document.createAttribute(attrName)方法創(chuàng)建並返回一個(gè)新的屬性節(jié)點(diǎn)。這個(gè)方法不是很常用,因?yàn)樘砑訉傩酝ǔJ褂?code>node.setAttribute(),。

let node = document.getElementById('content')
let attr = document.createAttribute('title')

attr.nodeValue = 'Hello JavaScript!'

node.setAttributeNode(attr)

上面的代碼會(huì)給div#content的元素添加一個(gè)title屬性,,而且這個(gè)title屬性的值為Hello JavaScript!

同樣的,document.createAttribute()雖然創(chuàng)建了屬性節(jié)點(diǎn),,如果不通過setAttributeNode()方法的話,,創(chuàng)建的屬性的節(jié)點(diǎn)是不會(huì)運(yùn)用到對(duì)應(yīng)的元素節(jié)點(diǎn)上的。該方法的返回值是一個(gè)Attr類型的節(jié)點(diǎn),。借助nodeValue給該節(jié)點(diǎn)賦值,,然後給該屬性節(jié)點(diǎn)設(shè)置對(duì)應(yīng)的屬性值。等同的效果,,常常使用setAttribute()方法來替代該方法,。後續(xù)我們會(huì)介紹到setAttribute()方法相關(guān)的知識(shí)。

document.adoptNode

document.adoptNode(externalNode) 從其他的 document 中獲取一個(gè)節(jié)點(diǎn)(externalNode),,並將該節(jié)點(diǎn)以及它的所有子節(jié)點(diǎn)從原文檔刪除, 並且它的 ownerDocument 屬性會(huì)變成當(dāng)前的 document,。之後你可以把這個(gè)節(jié)點(diǎn)插入到當(dāng)前文檔中,不常用,,瞭解即可,。

// 該函數(shù)用來從本文檔的第一個(gè) iframe 中獲取第一個(gè) element 元素,
// 並插入到當(dāng)前文檔樹中
function getEle(){
    var iframe = document.getElementsByTagName("iframe")[0],
    ele = iframe.contentWindow.document.body.firstElementChild;

    if(ele){
        document.body.appendChild(document.adoptNode(ele))
    }else{
        alert("沒有更多元素了")
    }
}
document.getElementById("move").onclick = getEle

注意,,該方法在同一 document 下的不同兩個(gè)元素中也可以使用,,可以實(shí)現(xiàn)從左邊欄列表中選取某些元素加載到右邊欄的功能。如果節(jié)點(diǎn)資源來自不同的源的時(shí)候,,調(diào)用 adoptNode 可能會(huì)失敗,。

有些情況下,將外部文檔的節(jié)點(diǎn)插入當(dāng)前文檔之前,你需要使用 document.importNode() 從外部文檔導(dǎo)入源節(jié)點(diǎn),,瞭解更多細(xì)節(jié),。

document.importNode

document.importNode(externalNode, deep) 這個(gè)接口也不常用,作用是拷貝外部文檔的一個(gè)節(jié)點(diǎn)(externalNode),。deep 表明是否要導(dǎo)入節(jié)點(diǎn)的後代節(jié)點(diǎn),,默認(rèn)為 false 不導(dǎo)入後代節(jié)點(diǎn)。

var iframe = document.getElementsByTagName("iframe")[0];
var oldNode = iframe.contentDocument.getElementById("myNode");
var newNode = document.importNode(oldNode, true);
document.getElementById("container").appendChild(newNode);

注意,,這個(gè)方法僅拷貝節(jié)點(diǎn),,此時(shí),節(jié)點(diǎn)存在於內(nèi)存中,,還需要插入當(dāng)前文檔中才能顯示,。

node.cloneNode

node.cloneNode(deep)方法返回該節(jié)點(diǎn)的一個(gè)副本,deep可選,,表明是否採(cǎi)用深度克隆,,如果為true,,則該節(jié)點(diǎn)的所有後代節(jié)點(diǎn)也都會(huì)被克隆,否則,,只克隆該節(jié)點(diǎn)本身,。

let node = document.getElementById('content')
let cloneNode = node.cloneNode(true)

cloneNode.id = "newId"

document.body.appendChild(cloneNode)

上面的這個(gè)小示例,克隆了div#content以及其所有後代節(jié)點(diǎn),,並且把新克隆的元素的id賦值為newId,,然後再把新克隆出來的所有節(jié)點(diǎn)重新插入body中。最終的效果如下:

上面的示例,,演示了,,使用node.cloneNode(true)可以克隆節(jié)點(diǎn)的所有後代節(jié)點(diǎn)以及其所有屬性。那麼對(duì)於綁定的事件是否也能被克隆呢,?還是通過示例來驗(yàn)證一下,,看看事件是否也會(huì)被克隆。

<div id="box">
    <button id="clone" onclick="console.log('Click Clone Button')">Clone Me!</button>
</div>

<div id="new"></div>

// cloneNode
let btn = document.getElementById('clone')
let box = document.getElementById('box')
let newDiv = document.getElementById('new')

newDiv.appendChild(box.cloneNode(true))

上面的示例使用了內(nèi)聯(lián)方式直接把事件寫在HTML標(biāo)籤上,。從結(jié)果我們可以看到綁定在HTML標(biāo)籤上的事件也被克隆了,。

接下來在上例的基礎(chǔ)上做一下調(diào)整,把內(nèi)聯(lián)方式換成綁定在節(jié)點(diǎn)對(duì)象上的事件:

let btn = document.getElementById('clone')
let box = document.getElementById('box')
let newDiv = document.getElementById('new')

btn.onclick = function () {
    console.log('click clone')
}

newDiv.appendChild(box.cloneNode(true))

從結(jié)果可以看出,,綁定在節(jié)點(diǎn)對(duì)象的事件在克隆的副本並不包含事件處理程序,。接著再做一下調(diào)整,使用addEventListener()方法把事件添加在節(jié)點(diǎn)上:

btn.addEventListener('click', function (){
    console.log('Click clone!')
})

得到的效果其實(shí)和上圖是一樣的,。也就是說,,克隆的時(shí)候,addEventListener()綁定的事件並沒有被克隆,。

從上面的示例可以證明,,副本節(jié)點(diǎn)只能綁定使用內(nèi)聯(lián)方式綁定的事件處理函數(shù),。簡(jiǎn)單點(diǎn)說,,只有內(nèi)聯(lián)在HTML元素的事件,才會(huì)被cloneNode()克隆,。

注意,,這個(gè)拷貝的節(jié)點(diǎn)並不在文檔中,需要自行添加到文檔中,。同時(shí)拷貝的節(jié)點(diǎn)有可能會(huì)導(dǎo)致節(jié)點(diǎn)的的 id 屬性重複,,最好修改新節(jié)點(diǎn)的 id,而 name 屬性也可能重複,,自行決定是否需要修改,。

節(jié)點(diǎn)修改

DOM節(jié)點(diǎn)修改有關(guān)的API有:

  • node.appendChild():插入一個(gè)新節(jié)點(diǎn)
  • node.insertBefore():插入一個(gè)新節(jié)點(diǎn)
  • node.removeChild():刪除一個(gè)節(jié)點(diǎn)
  • node.replaceChild():替換一個(gè)節(jié)點(diǎn)

其中node.appendChildnode.insertBefore屬於DOM中的新節(jié)點(diǎn)插入,而removeChild屬於DOM中的,,replaceChild屬於DOM中的,。這一節(jié),咱們只先聊增這一部分,對(duì)於刪和改,,我們後面會(huì)單獨(dú)介紹,。

node.appendChild

parentNode.appendChild(child)方法將一個(gè)節(jié)點(diǎn)child添加到指定的父節(jié)點(diǎn)parentNode的子節(jié)點(diǎn)列表的末尾。本方法返回值為要插入的這個(gè)節(jié)點(diǎn),。

let pEle = document.createElement('p')

pEle.textContent = '我是新添加的p元素'

document.body.appendChild(pEle)

上面的示例創(chuàng)建了一個(gè)新的段落元素pEle,,然後使用appendChild()將這個(gè)新創(chuàng)建的元素添加到body的最末尾。

使用appendChild()方法的時(shí)候有一點(diǎn)需要注意,。如果被插入的節(jié)點(diǎn)已經(jīng)存在文檔樹中,,則節(jié)點(diǎn)會(huì)被從原先的位置移除,並插入到新的位置,。當(dāng)然,,被移動(dòng)的元素被綁定的事件也會(huì)被同步過去,比如:

<div id="old">
    <p id="move">我是一個(gè)段落元素</p>
</div>

<div id="new"></div>

<button id="btn">創(chuàng)建元素</button>

let pEle = document.getElementById('move')
let newEle = document.getElementById('new')
let btnEle = document.getElementById('btn')

pEle.addEventListener('click', function() {
    console.log('click me!')
})

btnEle.addEventListener('click', function () {
    pEle.textContent = '我是新添加的p元素'
    newEle.appendChild(pEle)
})

如果要保留原來的這個(gè)子節(jié)點(diǎn)的位置,,則可以用 Node.cloneNode 方法複製出一個(gè)節(jié)點(diǎn)的副本,,然後再插入到新位置。這個(gè)方法只能將某個(gè)子節(jié)點(diǎn)插入到同一個(gè)文檔的其他位置,,如果你想跨文檔插入,,需要先調(diào)用document.importNode方法。還有,,如果appendChild()方法的參數(shù)是DocumentFragment節(jié)點(diǎn),,那麼插入的是DocumentFragment的所有子節(jié)點(diǎn),而不是DocumentFragment節(jié)點(diǎn)本身,。此時(shí),,返回值是一個(gè)空的DocumentFragment節(jié)點(diǎn)。

node.insertBefore

parentNode.insertBefore(child, referenceNode)方法將一個(gè)節(jié)點(diǎn)child插入作為父節(jié)點(diǎn)parentNode的一個(gè)子節(jié)點(diǎn),,並且位置在參考節(jié)點(diǎn)referenceNode之前,。

如果第二個(gè)參數(shù)referenceNodenull,則插入位置你父節(jié)點(diǎn)的末尾:

parentNode.insertBefore(node, null);
// 等價(jià)於
parentNode.appendChild(node);

注意,,第二個(gè)參數(shù)為null時(shí)不能省略,,否則會(huì)報(bào)錯(cuò)。

來看一個(gè)小示例:

<div id="parent">
    我是父節(jié)點(diǎn)
    <p id="child">我是舊的子節(jié)點(diǎn)</p>
</div>

<button id="btn">插入節(jié)點(diǎn)</button>

let parentEle = document.getElementById('parent')
let childEle = document.getElementById('child')
let btnEle = document.getElementById('btn')

btnEle.addEventListener('click', function () {
    let newEle = document.createElement('span')

    newEle.textContent = '我是新添加節(jié)點(diǎn)的文本內(nèi)容'

    parentEle.insertBefore(newEle, childEle)
})

使用這個(gè)方法可以模擬prependChild,,產(chǎn)生類似於appendChild(),,但是將節(jié)點(diǎn)插入作為指定父節(jié)點(diǎn)的第一個(gè)子節(jié)點(diǎn):

Node.prototype.prependChild = function (node) {
    return this.insertBefore(node, this.firstChild)
}

let parentEle = document.getElementById('parent')
let btnEle = document.getElementById('btn')


btnEle.addEventListener('click', function () {
    let newEle = document.createElement('p')

    newEle.textContent = '我是新添加節(jié)點(diǎn)的文本內(nèi)容'

    parentEle.prependChild(newEle)
})

其實(shí)這個(gè)效果和前面的效果是類似的。同樣的,,使用這個(gè)方法還可以模擬insertAfter,,將節(jié)點(diǎn)要插在父節(jié)點(diǎn)的某個(gè)子節(jié)點(diǎn)後面:

Node.prototype.insertAfter = function(node, referenceNode) {
    return this.insertBefore(node, referenceNode.nextSibling);
}

appendChild 類似,如果插入的節(jié)點(diǎn)是文檔中已經(jīng)存在的節(jié)點(diǎn),,則會(huì)移動(dòng)該節(jié)點(diǎn)到指定位置,,並且保留其綁定的事件,。

DOM的刪

DOM節(jié)點(diǎn)的刪除主要API是node.removeChild??梢允褂?code>parentNode.removeChild(child)刪除指定父節(jié)點(diǎn)parentNode的一個(gè)子節(jié)點(diǎn)child,,並返回被刪除的節(jié)點(diǎn)。

這個(gè)方法是要在被刪除的節(jié)點(diǎn)的父節(jié)點(diǎn)上調(diào)用的,,而不是在被刪除節(jié)點(diǎn)上調(diào)用的,,如果參數(shù)節(jié)點(diǎn)不是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn),removeChild 方法將報(bào)錯(cuò):

// 通過 parentNode 屬性直接刪除自身
var node = document.getElementById('deleteDiv');
if (node.parentNode) {
    node.parentNode.removeChild(node);
}

// 也可以封裝以下作為一個(gè)方法直接使用:
Node.prototype.remove = function(node) {
    if (node.parentNode) {
        return node.parentNode.removeChild(node);
    }
    throw new Error('Can not delete.');
}

node.remove();

使用這個(gè)方法也可以很簡(jiǎn)單的模擬 removeAllChild

Node.prototype.removeAllChild = function() {
    var deleteNode = []
    while (this.firstChild) {
        deleteNode.push(this.removeChild(this.firstChild));
    }
    return deleteNode;
}

被移除的這個(gè)子節(jié)點(diǎn)仍然存在於內(nèi)存中,,只是不在當(dāng)前文檔的 DOM 中,,仍然還可以被添加回文檔中。但是如果不使用一個(gè)變量保存這個(gè)節(jié)點(diǎn)的引用,,被刪除的節(jié)點(diǎn)將不可達(dá),,會(huì)在某次垃圾回收被清除。

DOM的改

parentNode.replaceChild(newChild, oldChild) 方法用指定的節(jié)點(diǎn)newChild替換當(dāng)前節(jié)點(diǎn)parentNode的一個(gè)子節(jié)點(diǎn)oldChild,,並返回被替換的節(jié)點(diǎn)oldChild,。

<div id="parent">
    <p id="child">我是舊的第一個(gè)子節(jié)點(diǎn)</p>
</div>

<button id="btn">替換節(jié)點(diǎn)</button>

let parentEle = document.getElementById('parent')
let oldEle = document.getElementById('child')
let btnEle = document.getElementById('btn')

btnEle.addEventListener('click', function () {
    let newEle = document.createElement('p')

    newEle.setAttribute('id', 'newChild')
    newEle.textContent = '我是新添加節(jié)點(diǎn)的文本內(nèi)容'

    parentEle.replaceChild(newEle, oldEle)
})

簡(jiǎn)單的總結(jié)一下

DOM中的節(jié)點(diǎn)操作對(duì)應(yīng)的主要API有:

  • appendChild():用於向childNodes列表的末尾添加一個(gè)節(jié)點(diǎn)。返回新增的節(jié)點(diǎn),。
  • insertBefore():接收兩個(gè)參數(shù):要插入的節(jié)點(diǎn)和作為參照的節(jié)點(diǎn),。插入節(jié)點(diǎn)後,被插入的節(jié)點(diǎn)會(huì)變成參照節(jié)點(diǎn)的前一個(gè)同胞節(jié)點(diǎn),。同時(shí)被方法返回,。
  • replaceChild():接收兩個(gè)參數(shù):要插入的節(jié)點(diǎn)和要替換的節(jié)點(diǎn)。要替換的節(jié)點(diǎn)將由這個(gè)方法返回並從文檔樹中移除,。同時(shí)由要插入的節(jié)點(diǎn)佔(zhàn)據(jù)其位置,。
  • removeChild():接收一個(gè)參數(shù),即要移除的節(jié)點(diǎn),。返回被移除的節(jié)點(diǎn),。

這四個(gè)方法都是操作的某個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn),也就是說,,要使用這幾個(gè)方法必須先取得父節(jié)點(diǎn),。另外並不是所有節(jié)點(diǎn)都有子節(jié)點(diǎn),如果在不支持子節(jié)點(diǎn)的節(jié)點(diǎn)上,,調(diào)用了這些方法,將會(huì)導(dǎo)致錯(cuò)誤,。

DOM的查

DOM節(jié)點(diǎn)中的查主要包括:查找元素(類似於CSS中的選擇器)和節(jié)點(diǎn)查找,。

查找元素

先來看DOM中怎麼查找到元素,也就是說選擇到你想要的元素,。在DOM中查找元素(選擇到想要的元素)對(duì)應(yīng)的API主要有:

  • document.getElementById(id):匹配特定id的元素
  • document.getElementsByName(name):根據(jù)給定的name 返回一個(gè)在 (X)HTML documentNodeList集合
  • document.getElementsByTagName(tagName):返回一個(gè)包括所有給定標(biāo)籤名稱的元素的HTML集合HTMLCollection
  • document.getElementsByClassName(className):返回包含了所有指定類名的子元素的類數(shù)組對(duì)象
  • document.querySelector(selector):返回文檔中與指定選擇器或選擇器組匹配的第一個(gè)Element
  • document.querySelectorAll(selector):返回與指定的選擇器組匹配的文檔中的元素列表,。返回的對(duì)象是NodeList

假設(shè)我們有一個(gè)簡(jiǎn)單的DOM文檔:

<div id="box">
    <h3>Title</h3>
    <ul class="list">
        <li class="item">Item1</li>
        <li class="item">Item2</li>
        <li class="item">Item3</li>
        <li class="item">Item4</li>
        <li class="item">Item5</li>
    </ul>
    <p id="intro">Intro ...</p>
</div>

為了更好的說明前面的幾個(gè)API,,後續(xù)中的示例,都會(huì)採(cǎi)用這個(gè)DOM結(jié)構(gòu),。其對(duì)應(yīng)的DOM樹不再繪製了,。

document.getElementById(id)

document.getElementById(id)返回的是一個(gè)Element對(duì)象,用來匹配文檔中指定的id元素,。如果沒有找到對(duì)應(yīng)的元素,,該方法會(huì)返回null。另外,,document.getElementById()方法不會(huì)搜索不在文檔中的元素,。當(dāng)創(chuàng)建一個(gè)元素,並且分配id後,,必須要使用insertBefore或其他類似的方法把元素插入到文檔之後才能使用document.getElementById()獲取到,。

來看一個(gè)示例:

let idEle = document.getElementById('intro')
let btnEle = document.getElementById('btn')

btnEle.addEventListener('click', function () {
    console.log(`能匹配到的:  ${idEle}`)
    console.log(idEle)

    let newEle = document.createElement('section')
    newEle.id = 'main'
    newEle.textContent = '我是新添加的元素'

    console.log(`未插入到DOM的新元素newEle: ${document.getElementById('main')}`)
    console.log(document.getElementById('main'))

    let box = document.getElementById('box')

    box.insertBefore(newEle, idEle)

    console.log(`插入到DOM的新元素newEle: ${document.getElementById('main')}`)
    console.log(document.getElementById('main'))

})

來看輸出的結(jié)果:

比如上面示例,通過document.getElementById()之後,,咱們獲取了DOM上的節(jié)點(diǎn),,這個(gè)時(shí)候可以對(duì)該節(jié)點(diǎn)做很多事情,比如查詢內(nèi)容和屬性,,或者其他任何操作,,甚至可以刪除它,克隆它,,或者將它移動(dòng)到DOM樹的其它節(jié)點(diǎn)上,。

注意,document.getElementById(id)中的id參數(shù)是有大小寫敏感的,,所以document.getElementById('Intro')無(wú)法獲取到元素<p id="intro">Intro ...</p>,。另外還有就是,如果文檔中有多個(gè)相同的id(這種情形一般不存在)時(shí),,只會(huì)返回第一個(gè),。

document.getElementsByName(name)

document.getElementsByName(name)將根據(jù)給定的name返回一個(gè)在document的節(jié)點(diǎn)列表集合。name屬性只有在HTML文檔中可用,。該方法返回的是一個(gè)NodeList集合,,這個(gè)集合包含name屬性為指定值的所有元素,比如<meta>,、<object>,,甚至那些不支持name屬性但是添加了name自定義屬性的元素也包含其中。

該方法常用於取得單選按鈕,。同樣也會(huì)返回HTMLCollection對(duì)象,。HTMLCollection對(duì)象可以通過length屬性訪問元素長(zhǎng)度,通過[]方括號(hào)語(yǔ)法訪問對(duì)象中的項(xiàng),。方括號(hào)中既可以是數(shù)字,,也可以是字符串索引值,。

document.getElementsByTagName(tagName)

document.getElementsByTagName(tagName)將會(huì)返回一個(gè)包括所有給定標(biāo)籤名稱tagName的元素的HTML集合HTMLCollection。整個(gè)文件結(jié)構(gòu)都會(huì)被搜索,,包括根節(jié)點(diǎn),。返回的HTML集合是動(dòng)態(tài)的,意味著它可以自動(dòng)更新來保持和DOM樹同步,,而不用再次調(diào)用document.getElementsByTagName(tagName),。

let liEle = document.getElementsByTagName('li')
let btnEle = document.getElementById('btn')

btnEle.addEventListener('click', function () {
    console.log(`能匹配到的:  ${liEle}`)
    console.log(liEle.length)

    Object.keys(liEle).forEach(key => {
        console.log(key, liEle[key])
    })
})

比如上面的示例,通過getElementsByTagName('li')獲取了文檔中所有的<li>元素,。其開始於一個(gè)具體的父元素並且從它自上而下遞歸地在DOM樹中搜索符合標(biāo)籤名稱參數(shù)的子元素,。剛才也說了,其返回的是一個(gè)動(dòng)態(tài)的HTMLCollection對(duì)象,。獲得這個(gè)對(duì)象之後,,可以對(duì)其做一些遍歷操作。比如上面使用Object.keys()遍歷出li

有關(guān)於JavaScript中對(duì)象遍歷相關(guān)的操作可以閱讀《如何遍歷JavaScript中對(duì)象屬性》和《對(duì)象屬性的枚舉》,。

有一點(diǎn)需要注意,,調(diào)用 getElementsByTagName() 的不是那個(gè)文件節(jié)點(diǎn) document,事實(shí)上是使用這個(gè)方法 element.getElementsByTagName(),。

document.getElementsByClassName(className)

document.getElementsByClassName(className)返回一個(gè)包含了所有指定類名的子元素的類數(shù)組對(duì)象,。當(dāng)在document對(duì)象上調(diào)用時(shí),會(huì)搜索整個(gè)DOM文檔,,包含根節(jié)點(diǎn),。你也可以在任意元素上調(diào)用getElementsByClassName()方法,它將返回的是以當(dāng)前元素為根節(jié)點(diǎn),,所有指定類名的子元素,。

比如,獲取所有classitem的元素:

document.getElementsByClassName('item')

如果你想獲取多個(gè)class的元素時(shí),,可以用空格來隔開,,比如說,同時(shí)獲取所有class同時(shí)包括btnbtn-lg的元素:

document.getElementsByClassName('btn btn-lg')

如果你想獲取某個(gè)元素的子節(jié)點(diǎn)中對(duì)應(yīng)class的元素時(shí),,你也可以像下面這樣操作:

document.getElementById('box').getElementsByClassName('item')

document.querySelector(selector)

document.querySelector(selector)方法可以幫助你選擇一個(gè)HTML元素,。如果選擇了多個(gè)HTML元素,其總是返回第一個(gè)元素,。它看起來像這樣:

document.querySelector('li')

使用這個(gè)方法可以通過id,、class以及標(biāo)籤元素,甚至是元素的一些屬性可以選擇一個(gè)元素,。

  • 使用一個(gè)id選擇元素,,需要在id前使用#
  • 使用一個(gè)class選擇元素,需要在class前使用.
  • 使用一個(gè)標(biāo)籤選擇元素,,可以直接把元素標(biāo)籤當(dāng)作選擇器

甚至為了更好的理解或者記憶,,只要滿足CSS的選擇器,那麼都可以被運(yùn)用於document.querySelector(selector)中的selector選擇器,。

document.querySelectorAll(selector)

document.querySelectorAll(selector)可以幫助你選擇多個(gè)元素,。這個(gè)方法中的selectordocument.querySelector()具有相同的語(yǔ)法。唯一不同的是,,你可以通過用逗號(hào),分隔來選擇多個(gè)元素,。

比如:

var matches = document.querySelectorAll("div.note, div.alert");

節(jié)點(diǎn)查找

DOM中節(jié)點(diǎn)共有12種類型,每種類型分別表示文檔中不同的信息標(biāo)記,。每個(gè)節(jié)點(diǎn)都擁有各自的特點(diǎn),、數(shù)據(jù)和方法,也與其他節(jié)點(diǎn)存在某種關(guān)係,。節(jié)點(diǎn)之間的關(guān)係構(gòu)成了層次,,而所有頁(yè)面標(biāo)記則表現(xiàn)為一個(gè)以特定節(jié)點(diǎn)為根節(jié)點(diǎn)的樹形結(jié)構(gòu)。用張圖來描述:

所有的節(jié)點(diǎn)都有這些屬性,,都是可以用於訪問相關(guān)的node節(jié)點(diǎn):

  • Node.childNodes: 訪問一個(gè)單元素下所有的直接子節(jié)點(diǎn)元素,,可以是一個(gè)可循環(huán)的類數(shù)組對(duì)象。該節(jié)點(diǎn)集合可以保護(hù)不同的類型的子節(jié)點(diǎn)(比如text節(jié)點(diǎn)或其他元素節(jié)點(diǎn)),。
  • Node.firstChild: 與childNodes數(shù)組的第一個(gè)項(xiàng)(Element.childNodes[0])是同樣的效果,,僅僅是快捷方式。
  • Node.lastChild: 與childNodes數(shù)組的最後一個(gè)項(xiàng)(Element.childNodes[Element.childNodes.length-1])是同樣的效果,,僅僅是快捷方式,。
  • Node.parentNode: 訪問當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn),父節(jié)點(diǎn)只能有一個(gè),,祖節(jié)點(diǎn)可以用Node.parentNode.parentNode的形式來訪問,。
  • Node.nextSibling: 訪問DOM樹上與當(dāng)前節(jié)點(diǎn)同級(jí)別的下一個(gè)節(jié)點(diǎn)。
  • Node.previousSibling: 訪問DOM樹上與當(dāng)前節(jié)點(diǎn)同級(jí)別的上一個(gè)節(jié)點(diǎn),。

用張圖來闡述,,會(huì)更清晰:

通過這張圖,理解起來就簡(jiǎn)單多了,,但有個(gè)非常重要的知識(shí)點(diǎn):那就是元素之間不能有空格,,如果ulli之間有空格的話,就會(huì)被認(rèn)為是內(nèi)容為空的text node節(jié)點(diǎn),,這樣ul.childNodes[0]就不是第一個(gè)li元素了,。相應(yīng)地,<p>的下一個(gè)節(jié)點(diǎn)也不是<ul>,,因?yàn)?code><p>和<ul>之間有一個(gè)空行的節(jié)點(diǎn),,一般遇到這種情況需要遍歷所有的子節(jié)點(diǎn)然後判斷nodeType類型。

根據(jù)上面的描述,我們可以把DOM中的節(jié)點(diǎn)相互之間存在著的各種關(guān)係分為:父子關(guān)係,,兄弟關(guān)係等:

父關(guān)係相關(guān)的API

  • parentNode:每個(gè)節(jié)點(diǎn)都有一個(gè)parentNode屬性,,它表示元素的父節(jié)點(diǎn)。Element的父節(jié)點(diǎn)可能是Element,,DocumentDocumentFragment,;如果不存在,則返回null
  • parentElement:返回元素的父元素節(jié)點(diǎn),,與parentNode的區(qū)別在於,,其父節(jié)點(diǎn)必須是一個(gè)Element元素,如果不是,,則返回null,;

子關(guān)係API

  • children:返回一個(gè)實(shí)時(shí)的HTMLCollection,子節(jié)點(diǎn)都是Element,;保存的是該節(jié)點(diǎn)的第一層元素子節(jié)點(diǎn)
  • childNodes:返回一個(gè)實(shí)時(shí)的NodeList,,表示元素的子節(jié)點(diǎn)列表,注意子節(jié)點(diǎn)可能包含文本節(jié)點(diǎn),、註釋節(jié)點(diǎn)等,;
  • firstChild:返回第一個(gè)子節(jié)點(diǎn),不存在返回null,,與之相對(duì)應(yīng)的還有一個(gè)firstElementChild,;
  • lastChild:返回最後一個(gè)子節(jié)點(diǎn),不存在返回null,,與之相對(duì)應(yīng)的還有一個(gè)lastElementChild,;

兄弟關(guān)係型API

  • previousSibling:節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn),如果不存在則返回null,。注意有可能拿到的節(jié)點(diǎn)是文本節(jié)點(diǎn)或註釋節(jié)點(diǎn),,與預(yù)期的不符,要進(jìn)行處理一下,。
  • nextSibling:節(jié)點(diǎn)的後一個(gè)節(jié)點(diǎn),,如果不存在則返回null。注意有可能拿到的節(jié)點(diǎn)是文本節(jié)點(diǎn),,與預(yù)期的不符,,要進(jìn)行處理一下。
  • previousElementSibling:返回前一個(gè)元素節(jié)點(diǎn),,前一個(gè)節(jié)點(diǎn)必須是Element,。
  • nextElementSibling:返回後一個(gè)元素節(jié)點(diǎn),後一個(gè)節(jié)點(diǎn)必須是Element,。

總結(jié)

DOM操作在JavaScript還是很重要的,,簡(jiǎn)單點(diǎn)說,所有的交互操作都是基於DOM來操作的。而DOM中的操作,,最為熟悉的就是對(duì)DOM的增,、刪、改,、查,。今天的內(nèi)容也就圍繞著這幾個(gè)方面展開,。因?yàn)樯婕暗降膬?nèi)容較多,,可能會(huì)有遺漏或者說零亂。如果你覺得上面不對(duì)之處,,還請(qǐng)路過大神指正,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多