JavaScript常見原生DOM操作API總結
JavaScript常見原生DOM操作API總結
目錄最近面試的時候被這個問題給卡了,所以抽時間好好複習一下。
幾種物件
1.1.Node
Node是一個介面,中文叫節點,很多型別的DOM元素都是繼承於它,都共享著相同的基本屬性和方法。常見的Node有element
,text
,attribute
,comment
,document
等(所以要注意節點
和元素
的區別,元素
屬於節點
的一種)。
Node有一個屬性nodeType
表示Node的型別,它是一個整數,其數值分別表示相應的Node型別,具體如下:
{
ELEMENT_NODE: 1, // 元素節點
ATTRIBUTE_NODE: 2, // 屬性節點
TEXT_NODE: 3, // 文字節點
DATA_SECTION_NODE: 4,
ENTITY_REFERENCE_NODE: 5,
ENTITY_NODE: 6,
PROCESSING_INSTRUCTION_NODE: 7,
COMMENT_NODE: 8, // 註釋節點
DOCUMENT_NODE: 9, // 文件
DOCUMENT_TYPE_NODE: 10,
DOCUMENT_FRAGMENT_NODE: 11, // 文件碎片
NOTATION_NODE: 12,
DOCUMENT_POSITION_DISCONNECTED: 1,
DOCUMENT_POSITION_PRECEDING: 2,
DOCUMENT_POSITION_FOLLOWING: 4,
DOCUMENT_POSITION_CONTAINS: 8,
DOCUMENT_POSITION_CONTAINED_BY: 16,
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 32
}
1.2.NodeList
NodeList 物件是一個節點的集合,一般由Node.childNodes
、document.getElementsByName
和document.querySelectorAll
返回的。
不過需要注意,Node.childNodes
、document.getElementsByName
返回的NodeList
的結果是實時的(此時跟HTMLCollection比較類似),而document.querySelectorAll
返回的結果是固定的,這一點比較特殊。
舉例如下:
var childNodes = document.body.childNodes;
console.log(childNodes.length); // 如果假設結果是“2”
document.body.appendChild(document.createElement('div'));
console.log(childNodes.length); // 此時的輸出是“3”
1.3.HTMLCollection
HTMLCollection是一個特殊的NodeList,表示包含了若干元素(元素順序為文件流中的順序)的通用集合,它是實時更新的,當其所包含的元素髮生改變時,它會自動更新。另外,它是一個偽陣列,如果想像陣列一樣操作它們需要像Array.prototype.slice.call(nodeList, 2)
這樣呼叫。
節點查詢API
-
document.getElementById
:根據ID查詢元素,大小寫敏感,如果有多個結果,只返回第一個; -
document.getElementsByClassName
:根據類名查詢元素,多個類名用空格分隔,返回一個HTMLCollection
。注意相容性為IE9+(含)。另外,不僅僅是document,其它元素也支援getElementsByClassName
方法; -
document.getElementsByTagName
:根據標籤查詢元素,*
表示查詢所有標籤,返回一個HTMLCollection
。 -
document.getElementsByName
:根據元素的name屬性查詢,返回一個NodeList
。 -
document.querySelector
:返回單個Node,IE8+(含),如果匹配到多個結果,只返回第一個。 -
document.querySelectorAll
:返回一個NodeList
,IE8+(含)。 -
document.forms
:獲取當前頁面所有form,返回一個HTMLCollection
;
節點建立API
節點建立API主要包括createElement
、createTextNode
、cloneNode
和createDocumentFragment
四個方法。
3.1.createElement
建立元素:
var elem = document.createElement("div");
elem.id = 'test';
elem.style = 'color: red';
elem.innerHTML = '我是新建立的節點';
document.body.appendChild(elem);
通過createElement
建立的元素並不屬於document
物件,它只是創建出來,並未新增到html文件中,要呼叫appendChild
或insertBefore
等方法將其新增到HTML文件中。
3.2.createTextNode
建立文字節點:
var node = document.createTextNode("我是文字節點");
document.body.appendChild(node);
3.3.cloneNode
克隆一個節點:node.cloneNode(true/false)
,它接收一個bool引數,用來表示是否複製子元素。
var from = document.getElementById("test");
var clone = from.cloneNode(true);
clone.id = "test2";
document.body.appendChild(clone);
克隆節點並不會克隆事件,除非事件是用<div onclick="test()"></div>
這種方式繫結的,用addEventListener
和node.onclick=xxx;
方式繫結的都不會複製。
3.4.createDocumentFragment
本方法用來建立一個DocumentFragment
,也就是文件碎片,它表示一種輕量級的文件,主要是用來儲存臨時節點,大量操作DOM時用它可以大大提升效能。
假設現有一題目,要求給ul新增10000個li,我們先用最簡單的拼接字串的方式來實現:
<ul id="ul"></ul>
<script>
(function()
{
var start = Date.now();
var str = '';
for(var i=0; i<10000; i++)
{
str += '<li>第'+i+'個子節點</li>';
}
document.getElementById('ul').innerHTML = str;
console.log('耗時:'+(Date.now()-start)+'毫秒'); // 44毫秒
})();
</script>
再換逐個append的方式,不用說,這種方式效率肯定低:
<ul id="ul"></ul>
<script>
(function()
{
var start = Date.now();
var str = '', li;
var ul = document.getElementById('ul');
for(var i=0; i<10000; i++)
{
li = document.createElement('li');
li.textContent = '第'+i+'個子節點';
ul.appendChild(li);
}
console.log('耗時:'+(Date.now()-start)+'毫秒'); // 82毫秒
})();
</script>
最後再試試文件碎片的方法,可以預見的是,這種方式肯定比第二種好很多,但是應該沒有第一種快:
<ul id="ul"></ul>
<script>
(function()
{
var start = Date.now();
var str = '', li;
var ul = document.getElementById('ul');
var fragment = document.createDocumentFragment();
for(var i=0; i<10000; i++)
{
li = document.createElement('li');
li.textContent = '第'+i+'個子節點';
fragment.appendChild(li);
}
ul.appendChild(fragment);
console.log('耗時:'+(Date.now()-start)+'毫秒'); // 63毫秒
})();
</script>
節點修改API
節點修改API都具有下面這幾個特點:
- 不管是新增還是替換節點,如果其原本就在頁面上,那麼原來位置的節點將被移除;
- 修改之後節點本身繫結的事件不會消失;
4.1.appendChild
這個其實前面已經多次用到了,語法就是:
parent.appendChild(child);
它會將child追加到parent的子節點的最後面。另外,如果被新增的節點是一個頁面中存在的節點,則執行後這個節點將會新增到新的位置,其原本所在的位置將移除該節點,也就是說不會同時存在兩個該節點在頁面上,且其事件會保留。
4.2.insertBefore
將某個節點插入到另外一個節點的前面,語法:
parentNode.insertBefore(newNode, refNode);
這個API個人覺得設定的非常不合理,因為插入節點只需要知道newNode和refNode就可以了,parentNode是多餘的,所以jQuery封裝的API就比較好:
newNode.insertBefore(refNode); // 如 $("p").insertBefore("#foo");
所以切記不要把這個原生API和jQuery的API使用方法搞混了!為了加深理解,這裡寫一個簡單的例子:
<div id="parent">
我是父節點
<div id="child">
我是舊的子節點
</div>
</div>
<input type="button" id="insertNode" value="插入節點" />
<script>
var parent = document.getElementById("parent");
var child = document.getElementById("child");
document.getElementById("insertNode").addEventListener('click', function()
{
var newNode = document.createElement("div");
newNode.textContent = "我是新節點";
parent.insertBefore(newNode, child);
}, false);
</script>
關於第二個引數:
- refNode是必傳的,如果不傳該引數會報錯;
- 如果refNode是undefined或null,則insertBefore會將節點新增到末尾;
4.3.removeChild
removeChild用於刪除指定的子節點並返回子節點,語法:
var deletedChild = parent.removeChild(node);
deletedChild指向被刪除節點的引用,它仍然存在於記憶體中,可以對其進行下一步操作。另外,如果被刪除的節點不是其子節點,則將會報錯。一般刪除節點都是這麼刪的:
function removeNode(node)
{
if(!node) return;
if(node.parentNode) node.parentNode.removeChild(node);
}
4.4.replaceChild
replaceChild用於將一個節點替換另一個節點,語法:
parent.replaceChild(newChild, oldChild);
節點關係API
DOM中的節點相互之間存在著各種各樣的關係,如父子關係,兄弟關係等。
5.1.父關係API
parentNode
:每個節點都有一個parentNode屬性,它表示元素的父節點。Element的父節點可能是Element,Document或DocumentFragment;parentElement
:返回元素的父元素節點,與parentNode的區別在於,其父節點必須是一個Element元素,如果不是,則返回null;
5.2.子關係API
children
:返回一個實時的HTMLCollection
,子節點都是Element,IE9以下瀏覽器不支援;childNodes
:返回一個實時的NodeList
,表示元素的子節點列表,注意子節點可能包含文字節點、註釋節點等;firstChild
:返回第一個子節點,不存在返回null,與之相對應的還有一個firstElementChild
;lastChild
:返回最後一個子節點,不存在返回null,與之相對應的還有一個lastElementChild
;
5.3.兄弟關係型API
previousSibling
:節點的前一個節點,如果不存在則返回null。注意有可能拿到的節點是文字節點或註釋節點,與預期的不符,要進行處理一下。nextSibling
:節點的後一個節點,如果不存在則返回null。注意有可能拿到的節點是文字節點,與預期的不符,要進行處理一下。previousElementSibling
:返回前一個元素節點,前一個節點必須是Element,注意IE9以下瀏覽器不支援。nextElementSibling
:返回後一個元素節點,後一個節點必須是Element,注意IE9以下瀏覽器不支援。
元素屬性型API
6.1.setAttribute
給元素設定屬性:
element.setAttribute(name, value);
其中name是特性名,value是特性值。如果元素不包含該特性,則會建立該特性並賦值。
6.2.getAttribute
getAttribute返回指定的特性名相應的特性值,如果不存在,則返回null:
var value = element.getAttribute("id");
樣式相關API
7.1.直接修改元素的樣式
elem.style.color = 'red';
elem.style.setProperty('font-size', '16px');
elem.style.removeProperty('color');
7.2.動態新增樣式規則
var style = document.createElement('style');
style.innerHTML = 'body{color:red} #top:hover{background-color: red;color: white;}';
document.head.appendChild(style);
7.3.window.getComputedStyle
通過element.sytle.xxx
只能獲取到內聯樣式,藉助window.getComputedStyle
可以獲取應用到元素上的所有樣式,IE8或更低版本不支援此方法。
var style = window.getComputedStyle(element[, pseudoElt]);
7.4.getBoundingClientRect
getBoundingClientRect
用來返回元素的大小以及相對於瀏覽器可視視窗的位置,用法如下:
var clientRect = element.getBoundingClientRect();
clientRect是一個DOMRect
物件,包含width、height、left、top、right、bottom,它是相對於視窗頂部而不是文件頂部,滾動頁面時它們的值是會發生變化的。
IE9以下瀏覽器不包含width和height,具體相容性可檢視這裡
參考
本文大部分內容是源於下面這篇文章:http://luopq.com/2015/11/30/javascript-dom/