通過(guò)前面的學(xué)習(xí)茶袒,我們都知道吏颖,DOM并不一定要由存在于HTML中的元素組成。我們僅需要使用幾行JavaScript代碼就可以將HTML元素添加到你的DOM中。而且你也有能力移動(dòng)周圍的元素丁恭,甚至刪除它們。除了這一切之外斋日,我們還可以動(dòng)態(tài)地創(chuàng)建和修改DOM中的元素牲览,而且這一DOM操作也是很重要的一個(gè)特性,同時(shí)也能滿足Web網(wǎng)站或應(yīng)用程序的一些操作恶守。
在今天的教程中第献,我們將繼續(xù)學(xué)習(xí)DOM中的一些基礎(chǔ)知識(shí)。在今天中兔港,我們將研究如何創(chuàng)建元素庸毫、刪除元素和克隆元素等。簡(jiǎn)單的說(shuō)衫樊,今天的內(nèi)容涉及到一些DOM的修改飒赃。
創(chuàng)建元素
正如前面提到的,動(dòng)態(tài)地創(chuàng)建HTML元素是DOM中非常常見(jiàn)的操作科侈。如果這是你第一次聽(tīng)到的载佳,也不用擔(dān)心,因?yàn)榻裉煲婕暗絻?nèi)容并不復(fù)雜臀栈。
在DOM的操作中蔫慧,我們可以使用createElement
方法來(lái)創(chuàng)建任何你想要?jiǎng)?chuàng)建的HTML元素。createElement
的工作方式非常簡(jiǎn)單权薯。你可以通過(guò)document
對(duì)象來(lái)調(diào)用createElement
姑躲,并將希望創(chuàng)建的HTML元素傳遞給它。比如下面的代碼盟蚣,你可以創(chuàng)建一個(gè)p
元素:
document.createElement('p')
如果運(yùn)行上面這行代碼黍析,它將會(huì)創(chuàng)建一個(gè)p
元素。需要注意的是刁俭,這個(gè)時(shí)候只是創(chuàng)建了一個(gè)p
元素橄仍,但在HTML中并看不到你所添加的p
元素。因?yàn)檫@個(gè)時(shí)候你只是創(chuàng)建了p
元素牍戚,并沒(méi)有放到你需要放置的地方乒融,言外之意酬屉,咱們動(dòng)態(tài)創(chuàng)建的p
元素現(xiàn)在只是漫無(wú)目的的四處游蕩!
出于這種原因是因?yàn)镈OM并沒(méi)有意識(shí)到這個(gè)元素的存在。為了使用元素成為DOM的一部分兽掰,需要做以下兩件事:
找到一個(gè)作為父元素的元素
使用
appendChild
方法横腿,并將您想要的元素添加到指定的元素中
比如下面這個(gè)示例:
上面的代碼中谓苟,通過(guò)document.body
訪問(wèn)的到父元素是body
元素。也就是說(shuō)彬祖,在body
元素上調(diào)用appendChild()
方法,并在這個(gè)方法中傳遞一個(gè)參數(shù)品抽,這個(gè)參數(shù)就是我們新創(chuàng)建的元素储笑,也就是示例中的newElement
≡残簦或許你已經(jīng)發(fā)現(xiàn)了突倍,在執(zhí)行document.body.appendChild(newElement)
之間,雖然使用document.createElement('p')
創(chuàng)建了p
元素盆昙,但在HTML中并看不到羽历。所以說(shuō),要在HTML文檔中想要看到document.createElemet()
方法創(chuàng)建的HTML元素淡喜,還需要使用類似appendChild()
這樣的方法秕磷。
下圖就是一個(gè)簡(jiǎn)單的DOM樹(shù),即使用createElement()
方法和appendChild()
給HTML新添加的p
元素(新添加的p
元素位于</body>
之前):
有一個(gè)細(xì)節(jié)需要注意炼团,使用appendChild()
函數(shù)將新創(chuàng)建的元素添加到指定的父元素下澎嚣,都將成為該父元素的最后一個(gè)子元素,也就是lastChild
瘟芝。比如說(shuō)上面的示例币叹,body
元素已經(jīng)有了h1
和script
元素。新創(chuàng)建的p
將作為body
的最后一個(gè)子元素模狭,被追加到script
之后,也就是</body>
標(biāo)簽之前踩衩。
前面提到過(guò)嚼鹉,使用createElement
創(chuàng)建的元素,是在DOM的世界中游離驱富,并未存在HTML的DOM世界當(dāng)中锚赤,而且也說(shuō)了,將新創(chuàng)建的HTML元素插入到DOM中褐鸥,appendChild
僅是一種方式线脚,其實(shí)我們還有別的方式。比如叫榕,想在h1
標(biāo)簽之后插入新創(chuàng)建的元素newElement
(事實(shí)是就是新創(chuàng)建的p
元素)浑侥,那么可以通過(guò)在指定的父節(jié)點(diǎn)上調(diào)用insertBefore()
函數(shù)來(lái)實(shí)現(xiàn)。insertBefore()
函數(shù)接受兩個(gè)參數(shù)晰绎。第一個(gè)參數(shù)是要插入的元素(比如newElement
)寓落,第二個(gè)參數(shù)是你想插入元素的位置,比如h1
元素之后(但在script
元素之前)荞下。那么我們可以這樣做:
運(yùn)行上面的代碼之后伶选,所得到的DOM樹(shù)如下所示:
或許你會(huì)說(shuō)史飞,既然有insertBefore()
方法,應(yīng)該也會(huì)有一個(gè)insertAfter()
方法仰税。事實(shí)證明构资,事實(shí)并非如此。在 DOM 操作中并沒(méi)有一個(gè)內(nèi)置的方法在DOM元素之后插入你想要插入的元素陨簇。在DOM的操作當(dāng)中吐绵,如果你想在一個(gè)元素之后插入你想要插入的元素,可以借助insertBefore()
函數(shù)來(lái)實(shí)現(xiàn)塞帐,比如在上面的示例中拦赠,我們要把新創(chuàng)建的元素添加到h1
和script
之間,當(dāng)然葵姥,使用insertBefore(newElement, scriptElement)
很容易實(shí)現(xiàn)荷鼠,但我們還可以借助其他的方式,來(lái)個(gè)曲線救國(guó)榔幸,比如h1
的下一個(gè)元素允乐,即h1Element.nextSibling
。那么我們可以這樣寫(xiě)我們的代碼:
用下圖來(lái)闡述削咆,更易于幫助大家理解:
就上面的示例而言牍疏,h1Element.nextSibling
調(diào)用到的是script
元素。將newElement
新創(chuàng)建的元素插入到script
元素之前拨齐,事實(shí)上也實(shí)現(xiàn)了在h1
元素之后插入新創(chuàng)建的元素newElement
鳞陨。如果目標(biāo)沒(méi)有兄弟元素呢?這個(gè)例子中的insertBefore
函數(shù)非常聰明瞻惋,它會(huì)自動(dòng)將你想要的元素追加到末尾厦滤。
閱讀到這里,你可能已經(jīng)清楚了歼狼,在JavaScript中操作DOM元素掏导,并沒(méi)有我們想像中要的insertAfter
函數(shù),但你可能很多時(shí)候需要這樣的一個(gè)特性羽峰,就是在指定元素之后插入你想要插入的元素趟咆,那么咱們可以自己定義一個(gè)函數(shù),來(lái)達(dá)到類似的一個(gè)功能梅屉。比如下面這個(gè)自定義的insertAfter
函數(shù):
function insertAfter(target, newElement) {
target.parentNode.insertBefore(newElement, target.nextSibling)
}
是的值纱,這樣的做法其實(shí)就是一種曲線救國(guó)的方式,但是它確實(shí)地有效坯汤。
除此之外计雌,將子元素添加到父元素的一種更通用的方法是通過(guò)實(shí)現(xiàn)父元素將子元素視為數(shù)組條目。要訪問(wèn)這一系列的子節(jié)點(diǎn)(children
)玫霎,需要有children
和childNodes
屬性凿滤。children
屬性只返回HTML元素妈橄,而childNodes
屬性返回的是更通用的節(jié)點(diǎn),這些節(jié)點(diǎn)代表了許多我們并不關(guān)系的東西翁脆。
刪除元素
你可能已經(jīng)意識(shí)到了眷蚓,既然能在DOM中創(chuàng)建一個(gè)新元素,那應(yīng)該也可以從DOM中刪除一元素吧反番。前面我們了解了createElement
方法創(chuàng)建元素沙热。接下來(lái),咱們來(lái)看看怎么從DOM中刪除一個(gè)已有元素罢缸。在DOM中篙贸,咱們可以使用removeChild
來(lái)刪除已有的DOM元素。
比如下面這個(gè)示例:
通過(guò)appendChild
方法將新創(chuàng)建的p
元素newElement
添加到body
元素中枫疆。如果閱讀了前面的內(nèi)容爵川,你應(yīng)該清楚其中的一切。如果要?jiǎng)h除這個(gè)元素息楔,則可以在body
中調(diào)用removeChild
方法寝贡,并將指針傳遞給希望刪除的元素,也就是newElement
元素值依。一旦執(zhí)行removeChild
圃泡,DOM就會(huì)不知道newElement
的存在。
需要注意的是愿险,如果希望從子節(jié)點(diǎn)的父節(jié)點(diǎn)中調(diào)用removeChild
來(lái)刪除你想要?jiǎng)h除的元素颇蜡。此方法不會(huì)遍歷DOM。假設(shè)沒(méi)有直接訪問(wèn)元素的父元素辆亏,并且不想浪費(fèi)時(shí)間來(lái)查找它澡匪。那可以通過(guò)parentNode
屬性,也可以很輕易的刪除該元素褒链。比如下面這個(gè)示例:
克隆元素
通過(guò)前面學(xué)習(xí),咱們知道怎么在DOM中添加一個(gè)新元素和刪除一個(gè)已有的元素疑苔。事實(shí)上甫匹,除此之外,還可以克隆一個(gè)元素惦费。
在DOM中克隆一個(gè)元素方式也很簡(jiǎn)單兵迅,在希望克隆的元素上調(diào)用cloneNode
函數(shù),并提供一個(gè)true
或false
的參數(shù)薪贫,以指定是否想克隆元素或元素及其所有子元素恍箭。
比如下面這個(gè)簡(jiǎn)單的示例:
上面的代碼把類名share
的div
賦值給一個(gè)share
的變量。接著使用cloneNode
函數(shù)克隆這個(gè)div
:
var shareClone = share.cloneNode(false);
上面的代碼把新克隆的內(nèi)容賦值給shareClone
瞧省。注意扯夭,這里傳給cloneNode
的值是false
鳍贾。這意味著只克隆了類名為share
的div
。
調(diào)用cloneNode
后的操作步驟與使用createElement
的步驟相同交洗。在下一行代碼中骑科,把新克隆的元素添加到div#footer
中。執(zhí)行上面的代碼之后构拳,DOM樹(shù)就變成下圖這樣:
接下來(lái)修改一下上面的代碼咆爽,把其中的false
修改為true
:
var shareClone = share.cloneNode(true);
執(zhí)行完這一行代碼,和前面結(jié)果不同之處是置森,這里克隆的不僅是div.share
這個(gè)元素斗埂,還克隆了該元素的所有后代元素。這個(gè)時(shí)候的DOM結(jié)構(gòu)如下:
正如你所看到的凫海,div.share
后面的子元素p
和img
元素也被克隆了呛凶。
替換元素
在DOM操作中,咱們還可以使用replaceChild()
方法用一個(gè)新的DOM節(jié)點(diǎn)來(lái)替換當(dāng)前節(jié)點(diǎn)盐碱。replaceChild
同樣的接收兩個(gè)參數(shù)把兔,第一個(gè)參數(shù)是新節(jié)點(diǎn),第二個(gè)參數(shù)就是舊節(jié)點(diǎn)(也就是需要被替代的元素節(jié)點(diǎn))瓮顽。比如前面的示例县好,用新創(chuàng)建的newElement
(使用createElement
創(chuàng)建的一個(gè)新p
元素)來(lái)替代已有的oldElement
(即,DOM中div
為share
的)暖混。
let newElement = document.createElement('p')
newElement.textContent = '新創(chuàng)建的p元素'let oldElement = document.querySelector('.share')
oldElement.parentNode.replaceChild(newElement, oldElement)
這個(gè)時(shí)候DOM的結(jié)構(gòu)變成了:
創(chuàng)建文本節(jié)點(diǎn)
DOM中除了元素節(jié)點(diǎn)之外缕贡,還有文本節(jié)點(diǎn),前面看到的相關(guān)操作都是針對(duì)DOM元素節(jié)點(diǎn)的拣播,比如說(shuō)創(chuàng)建元素晾咪,刪除元素和克隆元素等。這一小節(jié)贮配,來(lái)看文本節(jié)點(diǎn)的創(chuàng)建谍倦。
在DOM中如果需要?jiǎng)?chuàng)建文本節(jié)點(diǎn),可以使用document.createTextNode()
來(lái)創(chuàng)建新文本節(jié)點(diǎn)泪勒。這個(gè)方法接受一個(gè)參數(shù)昼蛀,即,要插入節(jié)點(diǎn)中的文本圆存。與設(shè)置已有文本節(jié)點(diǎn)的值一樣叼旋,作為參數(shù)的文本也將按照HTML或XML的格式進(jìn)行編碼。
let textNode = document.createTextNode('Hello JavaScript!')
在創(chuàng)建新文本節(jié)點(diǎn)的同時(shí)沦辙,也會(huì)為其設(shè)置ownerDocument
屬性夫植。不過(guò),除非把新節(jié)點(diǎn)添加到文檔樹(shù)中已經(jīng)存在的節(jié)點(diǎn)中油讯,否則我們不會(huì)在瀏覽器窗口中看到新節(jié)點(diǎn)详民。比如下面這個(gè)示例:
這個(gè)時(shí)候DOM變成這樣:
在《DOM節(jié)點(diǎn)屬性》和《理解DOM》都知道延欠,innerHTML
、innerText
和textContent
都可以創(chuàng)建DOM的文本節(jié)點(diǎn)之類阐斜。其中innerHTML
可以識(shí)別標(biāo)簽衫冻,而textContent
無(wú)法識(shí)別標(biāo)簽。事實(shí)上:
innerHTML
可以識(shí)別標(biāo)簽谒出,而createTextNode
會(huì)將內(nèi)容全部轉(zhuǎn)化為字符串innerText
隅俘、textContent
事實(shí)上和createTextNode
的基本用法是一樣的,都無(wú)法識(shí)別標(biāo)簽并轉(zhuǎn)化為HTML笤喳。但innerText
是一次修改为居,會(huì)將標(biāo)簽里所有內(nèi)容修改,createTextNode
可以逐條插入杀狡,避免整體修改蒙畴。
來(lái)簡(jiǎn)單的看一個(gè)小示例:
直接看結(jié)果吧:
prepend/append/before/after
其中prepend()
、append()
呜象、before()
和after()
等都是新增的DOM API膳凝。有關(guān)于它們更詳細(xì)的介紹,可以閱讀 @張?chǎng)涡?老濕寫(xiě)的博文恭陡,這篇博文章詳細(xì)介紹他們?cè)趺词褂靡约安町悺?/p>
這里只是簡(jiǎn)單的來(lái)看看這幾個(gè)API:
node.append(...nodes or string)
:在node
節(jié)點(diǎn)的末端附加節(jié)點(diǎn)或字符串node.prepend(...node or string)
:在node
節(jié)點(diǎn)的開(kāi)頭添加新插入的節(jié)點(diǎn)或字符串node.before(...node or string)
:在node
節(jié)點(diǎn)前插入節(jié)點(diǎn)或字符串node.after(...node or string)
:在node
節(jié)點(diǎn)后插入節(jié)點(diǎn)或字符串node.replaceWith(...node or string)
:用給定的節(jié)點(diǎn)或字符串來(lái)替找node
節(jié)點(diǎn)
來(lái)看一個(gè)簡(jiǎn)單的示例:
結(jié)果是這樣的:
下圖用來(lái)描述對(duì)就的方法:
insertAdjacentHTML/Text/Element
多年前蹬音,微軟提出了稱為insertAdjacentHTML()
的方法,將一個(gè)HTML或XML文本的指定字符串插入到DOM的特點(diǎn)位置休玩。insertAdjacentHTML()
接受兩個(gè)參數(shù)著淆。第一個(gè)定義了你想放HTML的位置,相對(duì)于目標(biāo)元素來(lái)說(shuō)拴疤∮啦浚可以是以下4個(gè)字符串類型的值之一:
beforebegin
: HTML頁(yè)面將被立即置于元素之前,作為同胞元素afterbegin
: HTML將被放置在元素內(nèi)呐矾,第一個(gè)子元素之前beforeend
: HTML將被放置在元素內(nèi)苔埋,最后一個(gè)子元素之后afterend
: HTML將被立即放置在元素之后,作為同胞元素
同樣蜒犯,這些都是字符串值组橄,不是關(guān)鍵字 ,所以他們必須放在單引號(hào)或雙引號(hào)內(nèi)愧薛。
第二個(gè)參數(shù)是你要插入的,也放在引號(hào)內(nèi)(否則這將是一個(gè)已定義的字符串型的變量)衫画。請(qǐng)注意毫炉,它應(yīng)該是一個(gè)字符串,而不是一個(gè)DOM元素或元素集合削罩,因此瞄勾,它可能僅僅是文本费奸,不是實(shí)際的標(biāo)簽。
如在 Mozilla Hacks介紹的一樣 进陡,insertAdjacentHTML()
有一些更常用的優(yōu)點(diǎn)愿阐,像innerHTML()
:它不會(huì)破壞現(xiàn)有的DOM元素,并且運(yùn)行得更好趾疚。
同樣來(lái)看一個(gè)小示例:
<div id="div"></div><script>
div.insertAdjacentHTML('beforebegin', '<p>Hello</p>');
div.insertAdjacentHTML('afterend', '<p>Bye</p>');</script>
瀏覽器渲染出來(lái)的結(jié)果如下:
這就是我們?nèi)绾螌⑷我釮TML附加到頁(yè)面的方法缨历。下圖是插入變量的示意圖:
我們可以看到,上圖和前面的圖片有相似之處糙麦。
插入點(diǎn)實(shí)際上是相同的辛孵,但是這個(gè)方法插入HTML。該方法有兩種使用方式:
elem.insertAdjacentText(where, text)
:插入的是文本字符串而不是HTMLelem.insertAdjacentElement(where, elem)
:插入的是HTML而不是文本字符串
它們的存在主要是為了使用語(yǔ)法“統(tǒng)一”赡磅。在實(shí)踐中魄缚,大部分時(shí)間只使用insertAdjacentHTML()
,因?yàn)樗梢圆迦胛谋竞虷TML元素焚廊,而append()
冶匹、prepend()
、before()
和after()
這幾個(gè)方法咆瘟,API更簡(jiǎn)短嚼隘,他們可以插入節(jié)點(diǎn)和文本片段。
有關(guān)于這方面更詳細(xì)的介紹搞疗,可以閱讀《DOM appendHTML
實(shí)現(xiàn)及insertAdjacentHTML
》和《JavaScript在box內(nèi)的一些實(shí)用方法》嗓蘑。
總結(jié)
今天我們主要學(xué)習(xí)了如何修改DOM。涉及到的內(nèi)容也比較多匿乃,簡(jiǎn)單的做一下小總結(jié)桩皿。
創(chuàng)建新節(jié)點(diǎn)的方法:
document.createElement(tag)
:創(chuàng)建一個(gè)HTML元素(通過(guò)你想要的HTML標(biāo)簽來(lái)創(chuàng)建)document.createTextNode(value)
: 創(chuàng)建一個(gè)文本節(jié)點(diǎn)(有點(diǎn)類似于textContent
)elem.cloneNode(deep)
:克隆節(jié)點(diǎn)(元素),其中deep
是一個(gè)布爾值幢炸,如果值為true
泄隔,那么elem
的所有后代都會(huì)被克隆
上面創(chuàng)建的元素和節(jié)點(diǎn),如果不去做插入的操作宛徊,是不是在DOM中渲染佛嬉,所以在DOMk 還有一些插入和刪除節(jié)點(diǎn)的DOM API。先來(lái)看從父節(jié)點(diǎn)做的相關(guān)操作:
parent.appendChild(node)
parent.insertBefore(node, nextSibling)
parent.removeChild(node)
parent.replaceChild(newElem, node)
上面這些方法返回都是node
闸天。再來(lái)看看給定一個(gè)節(jié)點(diǎn)和字符串的相關(guān)操作:
node.append(...nodes or strings)
node.prepend(...nodes or strings)
node.before(...nodes or strings)
node.after(...nodes or strings)
node.replaceWith(...nodes or strings)
node.remove()
除此之外暖呕,還可以根據(jù)把給出的一段HTML,通過(guò)elem.insertAdjacentHTML(where, html)
方法和下面的API苞氮,指定HTML片段插入的位置:
beforebegin
afterbegin
beforeend
afterend
有關(guān)于上述的DOM API具體的使用方式和細(xì)節(jié)湾揽,可以查閱前面相關(guān)的內(nèi)容。如果文章中有整理的不對(duì)之處,或者說(shuō)你有這方面更多的經(jīng)驗(yàn)库物,歡迎在下面的評(píng)論中與我們一起分享霸旗。