本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。
DocumnetTyp类型:
包含着与文档的doctype有关的所有信息,被保存在document.doctype对象中;
该类型继承自Node类;
特征:
- nodeType的值为10;
- nodeName的值为doctype的名称;
- nodeValue的值为null;
- parentNode是Document;
- 没有子节点;
console.log(document.doctype); // <!DOCTYPE html>
console.log(document.doctype.nextSibling); // <html>
console.log(document.doctype.nodeType); // 10 DOCUMENTTYPE_NODE类型
console.log(document.doctype.nodeName); // html
console.log(document.doctype.nodeValue); // null
console.log(document.doctype.parentNode); // #document
console.log(document.childNodes);
console.log(document.childNodes[0]); // <!DOCTYPE html>
DOM1级中,DocumentType对象不能动态创建,而只能通过解析文档的方式来创建;并把DocumentType对象保存在document.doctype中;
DOM1描述了解DocumentType对象的3个属性:
name:表示文档类型的名称,即出现在<!DOCTYPE之后的文本;
console.log(document.doctype.name); // html
entities:是由文档类型描述的实体的NamedNodeMap对象;
notations:是由文档类型描述的符号的NamedNodeMap对象
entities和notations均为NamedNodeMap类型,一般在XML中使用,在HTML中entities和notations都是空列表,所以通常为null或undefined;
DocumentFragment类型:
文档片段类型,在所有节点类型中,只有它在文档中没有对应的标识;
DOM规定文档片段(document fragment)称为“轻量级”的文档,它是一种特殊的Node,作为其他节点的临时的容器,可以包含和控制节点,但不会像完整的文档那样占用额外的资源;
特征:
- nodeType的值为11;
- nodeName的值为#document-fragment;
- nodeValue的值为null;
- parentNode的值为null;
虽然文档片段就跟标准的document一样,里面存储了由节点组成的文档结构,但因为文档片段是独立的,不属于文档的一部分,所以它的parentNode总是为null,并且,它的变化不会触发DOM树的重新渲染,也不会导致性能等问题;
文档片段的子节点可以是Element、ProcessingInstruction、Comment、Text、CDATASection或Entity-Reference;
文档片段类型本身没有定义属性和方法,其继承了Node的所有方法,通常用于执行那些针对文档的DOM操作;主要用其来保存将来可能会添加到文档中的节点;
利用document.createDocumentFragment()方法创建片段;
var fragment = document.createDocumentFragment();
console.log(fragment);
console.log(fragment.nodeType); // 11
console.log(fragment.nodeName); // #document-fragment
console.log(fragment.nodeValue); // null
console.log(fragment.parentNode); // null
console.log(fragment.childNodes); // NodeList
另外,其提供了文档片段的构造函数DocumentFragment(),使用它也可以返回一个文档片段对象,但IE不支持;
var fragment = new DocumentFragment();
可以通过appendChild()或insertBefore()将文档片段中的内容添加到文档中,同时,文档片段被清空;
var fragment = document.createDocumentFragment();
fragment.appendChild(document.createTextNode("零点程序员"));
console.log(fragment); // 有text节点
document.body.appendChild(fragment);
console.log(fragment); // 没有text节点,fragment已经被清空
如果将文档中的节点添加文档片段中,就会从文档树中移除该节点;
var fragment = document.createDocumentFragment();
var mydiv = document.getElementById("mydiv");
fragment.appendChild(mydiv); // mydiv会从文档树中被移除
console.log(fragment);
利用片段可以一次性的把多个节点添加到文档中,可以节省资源;
<ul id="myList"></ul>
<script>
var myList = document.getElementById("myList");
var fragment = document.createDocumentFragment();
for(var i=0; i<3; i++){
var li = document.createElement("li");
li.appendChild(document.createTextNode("课程:" + (i + 1)));
fragment.appendChild(li);
}
myList.appendChild(fragment);
// 或者
var courseArr = ["HTML","CSS","Javascript"];
var fragment = document.createDocumentFragment();
courseArr.forEach(function(c){
var li = document.createElement("li");
li.innerHTML = c;
fragment.appendChild(li);
});
myList.appendChild(fragment);
</script>
如:倒序排列一个节点的子节点:
function reverse(node){
var fragment = document.createDocumentFragment();
// 从后至前循环子节点,将每个子节点移动到文档片段中
// 如此,node的最后一个节点变成fragment的第一个节点
while(node.lastChild)
fragment.appendChild(node.lastChild);
// 把fragment的所有子节点一次性移加node中
node.appendChild(fragment);
}
var mylist = document.getElementById("mylist");
reverse(mylist);
文档片段其实在一个叫<template>元素中特别有用,该元素属于HTMLTemplateElement类型,其有个content 属性,就包含了一个DocumentFragment;只是可惜IE不支持;
在浏览器中会被渲染成
<template id="courseList">
#document-fragment
</template> -->
<template id="courseList">
<h2>Web前端开发</h2>
<ul>
<li>HTML</li>
<li>CSS</li>
<li>Javascript</li>
</ul>
</template>
<script>
var courseList = document.getElementById("courseList");
console.log(courseList.content); // #document-fragment
var p = document.createElement("p");
p.innerHTML = "该课程由大师哥王唯主讲";
courseList.content.appendChild(p);
document.body.appendChild(courseList.content);
</script>
Attr类型:
元素的特性在DOM中以Attr类型表示,就是存在于元素的attributes属性中的节点;其继承自Node类;尽管它也是节点,但特性却不被认为是DOM文档树的一部分;
特征:
- nodeType的值2;
- nodeName的值为特性的名称;
- nodeValue的值就是特性的值;
- parentNode的值为null;
- 在HTML没有子节点;在XML中子节点可以是Text或EntityReference;
var mydiv = document.getElementById("mydiv");
var attr = mydiv.getAttributeNode("name");
console.log(attr);
console.log(attr.nodeType); // 2
console.log(attr.nodeName); // name
console.log(attr.nodeValue); // mydiv
console.log(attr.parentNode); // null
Attr对象有3个属性:
name、value和specified;
console.log(attr.name); // name
console.log(attr.value); // mydiv
console.log(attr.specified); // true
Attr对象的相关方法:
document.createAttribute(name)方法:
创建并返回特性节点,传入特性的name名称;创建后可以通过为该节点的nodeValue属性设置该属性节点的属性值,也可以使用常用的 setAttribute()方法来替代该方法;
var attr = document.createAttribute("style");
attr.nodeValue = "color:red;";
setAttributeNode(attribute)方法:
为指定的Element添加属性节点,注意,该法是Element元素方法;创建特性节点后,必须使用元素的setAttributeNode()方法将特性节点添加到元素特性中;该方法会返回一个Attr属性节点;
var stylArr = mydiv.setAttributeNode(attr);
该特性对象不能被复用,一旦被添加到某个元素特性中,其他元素就不能再使用,但可以克隆,如:
var h2 = document.getElementsByTagName("h2")[0];
// h2.setAttributeNode(attr); // 异常
h2.setAttributeNode(attr.cloneNode());
这个方法很少使用,一般都是使用setAttribute()方法;
getAttributeNode(attrName)方法:返回指定元素的指定Attr节点;
用以上方法添加Attr节点后,可以通过attributes属性,getAttributeNode()方法,getAttribute()方法来访问;其中,attributes和getAttributeNode()会返回对应特性的Attr节点,而getAttribute()返回特性的值;
var mydiv = document.getElementById("mydiv");
var attr = document.createAttribute("style");
attr.nodeValue = "font-size:20px; color: blue;";
mydiv.setAttributeNode(attr);
console.log(mydiv.getAttribute("style"));
console.log(mydiv.getAttributeNode("style"));
console.log(mydiv.getAttributeNames());
这个方法也很少使用,一般使用getAttribute()方法;
removeAttributeNode(attrNode)方法:从当前的element(元素节点)删除指定的属性;
var delAttr = mydiv.removeAttributeNode(attr);
console.log(delAttr);
console.log(attr);
console.log(delAttr === attr);
// 注意,要先注释前面几行
if(mydiv.hasAttribute("style")){
var delAttr = mydiv.getAttributeNode("style");
delAttr = mydiv.removeAttributeNode(delAttr);
console.log(delAttr);
}
一般最常使用的是getAttribute()、setAttribute()和removeAttribute()方法,很少直接引用特性节点;
NodeList及HTMLCollection:
getElementsByName()返回的是NodeList,getElementsByTagName()返回是的HTMLCollection,document对象的集合也是HTMLCollection类型的;
理解NodeList及NamedNodeMap和HTMLCollection,是从整体上透彻理解DOM的关键所在;
这些对象都是类数组对象,都有length属性,也可以像真正的数组一样索引,但只能读不能写;
forEach():迭代;IE不支持;
nodelist.forEach(function(v,k,p){
console.log(v);
});
但可以借助数组的forEach()方法;
Array.prototype.forEach.call(nodelist, function(v,k,p){
console.log("k:" + k, "v:" + v, "p:" + p);
});
HTMLCollection接口属性和方法:
length:只读,返回集合中子元素的数目;
item():根据给定的索引,返回具体的节点;如果索引超出了范围,则返回 null;
namedItem():根据Id或name返回指定节点,根据name匹配只能作为后备,并且只有当被引用的元素支持name属性时才能被匹配,如果不存在符合给定name的节点,则返回null;
NodeList和HTMLCollection接口都定义了item()方法,接收一个整数,返回此索引处的元素;在实际开发中,没有必要使用这个方法,因为直接使用索引显得更加简单;
HTMLCollection定义了namedItem()方法,返回指定属性名的值,但也不常用,因为也是可以直接使用索引或常规属性来访问;
以上三个集合都是“动态的”,换句话说,每当文档结构发生变化时,它们都会得到更新;因此,它们始终都会保存着最新、最准确的信息;
从本质上说,所有NodeList对象都是在访问DOM文档时实时运行的查询;
如,以下代码会导致无限循环:
var divs = document.getElementsByTagName("div"),
i,
div;
for(i=0; i<divs.length; i++){
div = document.createElement("div");
document.body.appendChild(div);
}
如果想要迭代一个NodeList,最好是使用length属性初始化第二个变量,然后将迭代器与该变量进行比较,如:
var divs = document.getElementsByTagName("div"),
i,
len,
div;
for(i=0, len=divs.length; i<len; i++){
div = document.createElement("div");
document.body.appendChild(div);
}
或者直接对NodeList和HTMLCollection对象生成一个副本,在副本上进行一系列操作;
var snapshot = Array.prototype.slice.call(nodelist,0);
不要使用for...in或者for each...in来遍历一个NodeList对象中的元素,因为它们会把NodeList 对象中的 length 和 item 属性也遍历出来,这可能会导致脚本运行出错;此外,for...in也不能保证访问这些属性的顺序;
一般来说,应该尽量减少访问NodeList的次数,因为每次访问NodeList,都会运行一次基于文档的查询,所以,从一开始,可以将NodeList中取得的值缓存起来,然后再使用;
但NodeList也并不总是动态的,比如document.querySelectorAll就会返回一个静态 NodeList;