优秀的编程知识分享平台

网站首页 > 技术文章 正文

第44节 DocumentType、文档片段及Attr节点

nanyue 2024-07-25 05:35:24 技术文章 11 ℃

本内容是《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;

最近发表
标签列表