封裝 DOM 操作
DOM 將文檔(頁(yè)面結(jié)構(gòu))表示成一棵將樹,樹由元素節(jié)點(diǎn),屬性節(jié)點(diǎn),文本節(jié)點(diǎn)組成,而 <html> 即是樹的根。BOM 頂級(jí)窗口 window 包含了 document 屬性,可以理解為 BOM 包含了 DOM,她就是代表了當(dāng)前頁(yè)面的 HTMLDocument 對(duì)象,因此可以通過(guò)她來(lái)操作文檔內(nèi)容和結(jié)構(gòu)。
注意:操作節(jié)點(diǎn)時(shí)務(wù)必要降低性能開銷問(wèn)題。
關(guān)于 DOM 的幾個(gè)知識(shí)點(diǎn)
訪問(wèn)節(jié)點(diǎn)時(shí),可以遍歷節(jié)點(diǎn)組,依據(jù)以下三個(gè) nodeType 來(lái)匹配所需的節(jié)點(diǎn)類型。鑒于現(xiàn)代瀏覽器都提供了更便利的 API,所以下面的節(jié)點(diǎn)訪問(wèn)都是利用直接作用于元素節(jié)點(diǎn)的接口。
- nodeType 1 表示元素節(jié)點(diǎn)
- nodeType 2 表示屬性節(jié)點(diǎn)
- nodeType 3 表示文本節(jié)點(diǎn)
- DOM 操作主要是對(duì)三大節(jié)點(diǎn)進(jìn)行 添加、刪除、修改、替換。其中就涉及了節(jié)點(diǎn)的訪問(wèn)操作,包括了訪問(wèn)子節(jié)點(diǎn)、父節(jié)點(diǎn)、兄弟節(jié)點(diǎn)、文本節(jié)點(diǎn)、屬性節(jié)點(diǎn)等。
下面的示例僅包含上面所談到的知識(shí)點(diǎn)。
getElement 接口 $
- 該接口便利于我們通過(guò) [類名|ID名|標(biāo)簽名] 直接獲取相匹配的節(jié)點(diǎn)對(duì)象
function $(str) {
str = str ? str.trim() : ''
if (!str) {
return false
}
var tag = str.charAt(0)
var name = str.substr(1)
switch(tag) {
case '#':
return document.getElementById(name)
break
case '.':
return document.getElementsByClassName(name)
break
default:
return document.getElementsByTagName(str)
}
}
關(guān)系型節(jié)點(diǎn)訪問(wèn)接口
關(guān)系型在這里表示為在 DOM 樹中沿樹枝,樹葉,上下左右方向訪問(wèn)的意思。
var el = typeof str === "string" ? $(str) : str
這段代碼在每個(gè)訪問(wèn)接口都需求到,即傳入 [類名|ID名|標(biāo)簽名|DOM object] 都可以,下面拿訪問(wèn)父級(jí)節(jié)點(diǎn)(向上訪問(wèn)只能是元素節(jié)點(diǎn))來(lái)說(shuō)。
function parentNode(str) {
var el = typeof str === "string" ? $(str) : str
// IE el.parentElement
return el.length ? el[0].parentNode : el.parentNode
}
對(duì),如你所見,這里 封裝 的很多的接口都是類似的,只是目標(biāo)不同,所以拿幾個(gè)獻(xiàn)丑就夠了!
function firstChildNode(str) {
var el = typeof str === "string" ? $(str) : str
// firstChild 包含文本節(jié)點(diǎn)
return el.length ? el[0].firstElementChild : el.firstElementChild
}
之前對(duì)瀏覽器提供的關(guān)系型節(jié)點(diǎn)訪問(wèn) API 一直分不清楚,現(xiàn)在也記不清,所以...,發(fā)揮爛筆頭的用處吧,區(qū)分開哪個(gè) api name 會(huì)包含元素節(jié)點(diǎn)或同時(shí)文本節(jié)點(diǎn)。結(jié)果發(fā)現(xiàn)子節(jié)點(diǎn) children 和父節(jié)點(diǎn) parentNode 特殊點(diǎn)(元素節(jié)點(diǎn)),含 *Nodes 與 *Child 的幾乎都是包括文本節(jié)點(diǎn)的。而含 *Element* 都是元素節(jié)點(diǎn),于是我將它們都改成了純?cè)毓?jié)點(diǎn)接口。
lastElementChild previousElementSibling nextElementSibling 三個(gè)都是訪問(wèn)元素節(jié)點(diǎn)的,感覺還是不好記,易混淆。
節(jié)點(diǎn)操作
來(lái)看一個(gè)不實(shí)用的添加節(jié)點(diǎn)的接口,其功能是創(chuàng)建一新節(jié)點(diǎn),然后在給定元素的子集合里將新節(jié)點(diǎn)追加到尾部或添加到頭部。
/**
* @param {string} tagName 要?jiǎng)?chuàng)建的標(biāo)簽名
* @param {string} msg 新建標(biāo)簽名下的文本節(jié)點(diǎn) | 元素和文本節(jié)點(diǎn)并存
* @param {boolean} head 是否追加到頭部
* @param {boolean} tag 是否使元素、屬性、文本節(jié)點(diǎn)并存
*/
function addNode(str, tagName, msg, head, tag) {
var el = typeof str === "string" ? $(str) : str
var newNode = document.createElement(tagName.trim())
head = typeof head === 'boolean' ? head : false
tag = typeof tag === 'boolean' ? tag : true
el = el.length ? el[0] : el
if (msg) {
if (tag) {
newNode.innerHTML = msg
} else {
// '<code>Be happy.</code>' print as string.
newNode.innerText = msg
}
}
if (head) {
el.insertBefore(newNode, firstChildNode(el))
} else {
el.appendChild(newNode)
}
}
addNode('#ad', 'h3', '<strong>買十送零活動(dòng)</strong>', true)
addNode('.box', 'div', 'add <div></div> string', false, false)
此函數(shù)功能為刪除給定元素下符合條件的元素節(jié)點(diǎn),傳一個(gè)參數(shù)則表示全刪。條件可以是指定要?jiǎng)h除的標(biāo)簽名和刪除第幾個(gè)。有幾個(gè)自定義的接口,看名字就可以知道她是能干嘛的,如 childNodes。
/**
* @param {string} nodeName 指定標(biāo)簽名的約束條件
* @param {number} nth 指定索引的約束條件
*/
function delNode(str, nodeName, nth) {
var children = childNodes(str, true)
if (nth && children.length > 0) {
for (var i = 0, n = 1; i < children.length; i++) {
if (children[i].localName === nodeName) {
if (n === nth) {
// 找到了
parentNode(children[i]).removeChild(children[i])
break
}
n++
}
}
return
}
// 不能在循環(huán)里面直接刪,會(huì)導(dǎo)致索引變更
var delArr = []
for (var i = 0; i < children.length; i++) {
if (!nodeName) {
delArr.push(children[i])
} else if (children[i].localName === nodeName) {
// or nodeName 該方法返回的都是大寫的
delArr.push(children[i])
}
}
delArr.forEach(function (value) {
parentNode(value).removeChild(value)
})
}
delNode('#msg', 'div', 3) // 將ID為msg元素下的第三個(gè) div 刪掉
delNode('#msg', 'div') // 將ID為msg元素下 div 全刪掉
屬性節(jié)點(diǎn)操作
直接上代碼,看注釋吧!
/**
* 設(shè)置元素自定義屬性值
* @param {object} item 屬性鍵值對(duì)
*/
function setAttr(str, item) {
var el = typeof str === "string" ? $(str) : str
el = el.length ? el[0] : el
for (var key in item) {
// or document.createAttribute(key) 創(chuàng)建屬性節(jié)點(diǎn)添加
// 自定義屬性若包含樣式,DOM 樣式是不會(huì)生效的
el.setAttribute(key, item[key])
}
}
/**
* 添加元素指定的類名
* @param {string} name 待添加的類名
*/
function addClass(str, name) {
var el = typeof str === "string" ? $(str) : str
el = el.length ? el[0] : el
if (!el.classList.contains(name)) {
// or className + ' newClassName' 字符串追加
el.classList.add(name)
}
}
The last one.
/**
* 設(shè)置元素的行內(nèi)樣式屬性值
* @param {object} obj 樣式屬性鍵值
*/
function setStyle(str, obj) {
var el = typeof str === "string" ? $(str) : str
el = el.length ? el[0] : el
// el.style.* 只能設(shè)置行內(nèi)樣式
for (var key in obj) {
el.style[key] = obj[key]
}
}
setStyle('.wrapper', {color: 'pink', opacity: '0.5'})
以上所記錄的只是瀏覽器提供的 API 里的冰山一角,無(wú)法在這兒一一列舉,我們只需認(rèn)識(shí)到她們大多都能夠一針見血,有著 WYSIWYG 神話般的效果,待需要時(shí),再現(xiàn)學(xué)現(xiàn)用就好啦。
End.
若有不足,還請(qǐng)高人指教。