一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情。
节点克隆
使用 DOM 方法更新页面内容的另一个途径是克隆已有元素,而不是创建新元素就是使用element.cloneNode()代替document.createElement()。在大多数浏览器中,节点克隆更有效率。
HTML 集合
HTML 集合包含了 DOM 节点引用的类数组对象。以下方法的返回值是一个集合,类似数组的列表。
- document.getElementByName()
- document.getElementByClassName()
- document.getElementByTagName()
- document.images
- document.links
- document.forms
- document.forms[0].elements
为了演示集合的实时性,考虑以下代码:
let alldivs = document.getElementsByTagName('div');
for(let i = 0; i < alldivs.length; i++){
document.body.appendChild(document.createElement('div'))
}
实际上是一个死循环,每次迭代时length都会增加,它反映出的是底层文档的当前状态。像这样便利HTMNL集合可能会导致逻辑错误而且也很慢。在循环的条件控制语句中读取数组的length属性是不推荐的做法。读取一个集合的length要比读取普通数组的length要慢很多。
很多情况下如果只需要便利一个相对较小的集合,那么缓存length就够了,但由于遍历数组比遍历集合快,因此如果先将集合元素拷贝到数组中,那么访问它的属性会更快。
一般来说,对于任何类型的 DOM 访问,需要多次访问同一个 DOM 属性或方法需要多次访问时,最好使用一个局部变量缓存此成员。当遍历一个集合时,第一优化原则是把集合村在局部变量中,把length缓存在循环外部,然后用局部变量代替这些需要多次读取的元素。
// 较慢
function collectionGlobal(){
let coll = document.getElementsByTagName('div'),
len = coll.length,
name = '';
for(let i = 0; i < len; i++){
name = document.getElementsByTagName('div')[i].nodeName;
name = document.getElementsByTagName('div')[i].nodeType;
name = document.getElementsByTagName('div')[i].tagName;
}
return name;
}
// 较快
function collectionlocal(){
let coll = document.getElementsByTagName('div'),
len = coll.length,
name = '';
for(let i = 0; i < len; i++){
name = coll[i].nodeName;
name = coll[i].nodeType;
name = coll[i].tagName;
}
return name;
}
// 最快
function collectionlocal(){
let coll = document.getElementsByTagName('div'),
len = coll.length,
name = '',
el = null;
for(let i = 0; i < len; i++){
el = coll[i]
name = el.nodeName;
name = el.nodeType;
name = el.tagName;
}
return name;
}
遍历 DOM
DOM API 提供了多种方法来读取文档结构中的特定部分。当你需要从多种方案中选择时,最好为特定操作选择最高效的 API。
通常你需要从某一个 DOM 元素开始,操作周围的元素,或者递归查找所有子节点。你可以用 childNodes 得到元素集合,或者用 nextSibling 来获取每个相邻元素。
function testNextSibling(){
let el = document.getElementById('myDiv'),
ch = el.firstChild,
name = '';
do{
name = ch.nodeName;
} while(ch = ch.nextSibling);
return name;
}
function testChildNodes(){
let el = document.getElementById('myDiv'),
ch = el.childNodes,
len - ch.length,
name = '';
for(let i = 0; i < len; i++){
name = ch[count].nodeName;
}
return name;
}
childNodes 是个元素集合, 因此在循环中注意缓存 length 属性。
在不同浏览器两个方法的运行时间几乎相等。
元素节点
DOM 元素属性诸如 childNodes, firstChild 和 nextSibling 并不区分元素节点和其他类型节点,比如注释和文本节点。在某些情况下,需要访问元素节点,因此在循环中很可能需要检擦返回的节点类型并过滤掉非元素节点。 使用children代替childNodes 会更快,因为集合项更少。HTML源码中的空白实际上是文本节点,而且它并不包含在children集合中。
对 DOM 中的特定元素操作时,开发者通常需要得到比 getElementById()和 getElementsByTagName()更好的控制。最新的浏览器也提供了一个名为querySelectorAll()的原生 DOM 方法。