DOM节点类型
一般地,节点至少拥有nodeType、nodeName和nodeValue三个基本属性。节点类型不同,三个属性的值也不相同。
- nodeType
nodeType属性返回节点类型的常数值。不同的类型对应不同的常数值,12种类型分别对应1到12的常数值。
元素节点 Node.ELEMENT_NODE(1) 属性节点 Node.ATTRIBUTE_NODE(2) 文本节点 Node.TEXT_NODE(3) CDATA节点 Node.CDATA_SECTION_NODE(4) 实体引用名称节点 Node.ENTRY_REFERENCE_NODE(5) 实体名称节点 Node.ENTITY_NODE(6) 处理指令节点 Node.PROCESSING_INSTRUCTION_NODE(7) 注释节点 Node.COMMENT_NODE(8) 文档节点 Node.DOCUMENT_NODE(9) 文档类型节点 Node.DOCUMENT_TYPE_NODE(10) 文档片段节点 Node.DOCUMENT_FRAGMENT_NODE(11) DTD声明节点 Node.NOTATION_NODE(12DOM定义了一个Node接口,这个接口在JavaScript中是作为Node类型实现的,而在IE8-所有DOM对象都是以COM对象的形式实现的。 所以,IE8-浏览器并不支持Node对象的写法: 在标准浏览器下返回1,而在IE8-浏览器中报错,提示Node未定义 console.log(Node.ELEMENT_NODE); // 1
- nodeName:返回节点的名称(标签名)。
- nodeValue:返回或设置当前节点的值,格式为字符串。 // 元素节点的 nodeValue 始终是 null
元素节点
元素节点element对应网页的HTML标签元素。元素节点的节点类型nodeType值是1,节点名称nodeName值是大写的标签名,nodeValue值是null。
以body元素为例
// 1 'BODY' null console.log(document.body.nodeType,document.body.nodeName,document.body.nodeValue) console.log(Node.ELEMENT_NODE === 1); // true
元素节点Element非常常用,是DOM文档树的主要节点;元素节点是HTML标签元素的DOM化结果。元素节点主要提供了对元素标签名、子节点及特性的访问。
01. 特征
元素节点的三个node属性——nodeType、nodeName、nodeValue分别是1、元素的大写标签名和null,其父节点parentNode指向包含该元素节点的元素节点Element或文档节点Document。
[注意]要访问元素的标签名可以使用nodeName,也可以使用tagName属性,这两个属性会返回相同的值。
<div id="test">123</div> <script> console.log(test.nodeType);//1 console.log(test.nodeName);//'DIV' console.log(test.nodeValue);//null console.log(test.parentNode);//<body> console.log(test.childNodes);//[text] console.log(test.tagName,test.tagName === test.nodeName);//'DIV' true </script>
02. 子节点
元素可以有任意数目的子节点和后代节点,因为元素可以是其他元素的子节点。元素的childNodes属性中包含了它的所有子节点,这些子节点可能是元素、文本、注释、处理指令节点。
<ul class="list" id="list">
<li class="in"></li>
<li class="in"></li>
</ul>
<script>
var oList = document.getElementById('list');
//IE8-浏览器返回2,其他浏览器返回5。因为IE8-浏览器子节点中不包含空白文本节点
//关于空白文本节点的详细内容[移步至此](http://www.cnblogs.com/xiaohuochai/p/5815193.html#anchor2)
console.log(oList.childNodes.length)
</script>
兼容
可以通过检查nodeType属性来只获取元素节点。
<ul class="list" id="list">
<li class="in"></li>
<li class="in"></li>
</ul>
<script>
var oList = document.getElementById('list');
var children = oList.childNodes;
var num = 0;
for(var i = 0; i < children.length; i++){
if(children[i].nodeType == 1){
num++;
}
}
console.log(num);//2
</script>
03. 特性操作
每个元素都有一个或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。操作特性的DOM方法主要有hasAttribute()、getAttribute()、setAttribute()、removeAttribute()四个,可以针对任何特性使用,包括那些以HTMLElement类型属性的形式定义的特性
hasAttribute()
hasAttribute()方法返回一个布尔值,表示当前元素节点是否包含指定属性
[注意]IE7-浏览器不支持hasAttribute()方法。
<div id="test" class="class1"></div>
<script>
console.log(test.hasAttribute('class'));//true
console.log(test.hasAttribute('title'));//false
</script>
getAttribute()
getAttribute()方法用于取得特性的值,如果给定名称的特性不存在或无参数则返回null。
<div id="test" class="class1"></div>
<script>
console.log(test.getAttribute('class'));//'class1'
console.log(test.getAttribute('title'));//null
console.log(test.getAttribute('b'));//null
console.log(test.getAttribute(''));//null
</script>
[注意]元素特性和对象属性并不相同。
setAttribute()
setAttribute()方法接受两个参数:要设置的特性名和值,如果已经存在,则替换现有的值。如果特性不存在,setAttribute()则创建该属性并设置相应的值。该方法无返回值。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
oBox.setAttribute("id","test");
//注意获取oBox.id时并不会报错,因为oBox保存的是当时id为box的对象,也就是现在id为test的对象
console.log(oBox.id);//test
</script>
[注意] 通过setAttrbute()方法设置的特性名会统一转换成小写形式。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
oBox.setAttribute("ABC","test");
console.log(oBox.getAttribute("ABC"));//test
console.log(oBox.getAttribute("abc"));//test
</script>
bug
IE7-浏览器设置class、style、for、cellspacing、cellpadding、tabindex、readonly、maxlength、rowspan、colspan、usemap、frameborder、contenteditable这13个特性没有任何效果。
<style>
.testClass{
font-size: 30px;
}
</style>
<div id="box">123</div>
<script>
//IE7-浏览器下没有任何效果,其他浏览器出现红色背景及30px的文字大小
var oBox = document.getElementById('box');
oBox.setAttribute("class","testClass");
oBox.setAttribute("style","height: 100px; background: red;")
</script>
可以利用IE7-浏览器下对象属性和元素特性的混淆bug来设置。
<style>
.testClass{
font-size: 30px;
}
</style>
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
oBox.setAttribute("class","testClass");
oBox.setAttribute("style","height: 100px; background: red;");
//IE7下oBox.className的值为undefined
if(!oBox.className){
oBox.setAttribute("className","testClass");
oBox.style.setAttribute("cssText","height: 100px; background: red;");
}
</script>
removeAttribute()
removeAttribute()方法用于彻底删除元素的特性,这个方法不仅会彻底删除元素的特性值,还会删除元素特性。该方法无返回值。
<div class="box" id="box"></div>
<script>
var oBox = document.getElementById('box');
console.log(oBox.getAttribute("id"));//box
console.log(oBox.removeAttribute("id"));//undefined
console.log(oBox.getAttribute("id"));//null
</script>
04. attributes属性
元素节点Element是唯一一个使用attributes属性的DOM节点类型。attributes属性中包含一个NamedNodeMap,与NodeList类似,也是一个动态的集合。元素的每一个特性都由一个Attr节点表示,每个节点都保存在NamedNodeMap对象中,每个节点的nodeName就是特性的名称,节点的nodeValue就是特性的值
attributes属性包含以下四个方法:
getNamedItem(name)
getNamedItem(name)方法返回nodeName属性等于name的节点。
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
var oBox = document.getElementById('box');
console.log(oBox.attributes);//NamedNodeMap {0: class, 1: id, 2: name, 3: index, 4: title}
console.log(oBox.attributes.getNamedItem("index"));//index='123'
console.log(oBox.attributes.getNamedItem("index").nodeName);//'index'
console.log(oBox.attributes.getNamedItem("index").nodeValue);//'123'
console.log(oBox.attributes.index);//index='123'
</script>
removeNamedItem(name)
removeNamedItem(name)方法从列表中移除nodeName属性等于name的节点,并返回该节点。
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
var oBox = document.getElementById('box');
console.log(oBox.attributes);//NamedNodeMap {0: class, 1: id, 2: name, 3: index, 4: title}
console.log(oBox.attributes.getNamedItem("index"));//index='123'
console.log(oBox.attributes.removeNamedItem("index"));//index='123'
console.log(oBox.attributes.getNamedItem("index"));//null
</script>
setNamedItem(node)
setNamedItem(node)方法向列表中添加节点,该方法无返回值。
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
var oBox = document.getElementById('box');
console.log(oBox.attributes);//NamedNodeMap {0: class, 1: id, 2: name, 3: index, 4: title}
var oldItem = oBox.attributes.removeNamedItem("index");
console.log(oBox.attributes.getNamedItem("index"));//null
console.log(oldItem);//index='123'
console.log(oBox.attributes.setNamedItem(oldItem));//null
console.log(oBox.attributes.getNamedItem("index"));//index='123'
</script>
item(pos)
item(pos)方法返回位于数字pos位置处的节点,也可以用方括号法[]简写。
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
var oBox = document.getElementById('box');
console.log(oBox.attributes);//NamedNodeMap {0: class, 1: id, 2: name, 3: index, 4: title}
console.log(oBox.attributes.item(2));//name="abc"
console.log(oBox.attributes[2]);//name="abc"
</script>
遍历
attributes属性主要用于特性遍历。在需要将DOM结构序列化为HTML字符串时,多数都会涉及遍历元素特性。
function outputAttributes(element){
var pairs = new Array(),attrName,attrValue,i,len;
for(i = 0,len=element.attributes.length;i<len;i++){
attrName = element.attributes[i].nodeName;
attrValue = element.attributes[i].nodeValue;
pairs.push(attrName +"=\"" + attrValue + "\"");
}
return pairs.join(" ");
}
针对attributes对象中的特性,不同浏览器返回的顺序不同。
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
function outputAttributes(element){
var pairs = new Array(),attrName,attrValue,i,len;
for(i = 0,len=element.attributes.length;i<len;i++){
attrName = element.attributes[i].nodeName;
attrValue = element.attributes[i].nodeValue;
pairs.push(attrName +"=\"" + attrValue + "\"");
}
return pairs.join(" ");
}
//(chrome\safari)class="box" id="box" name="abc" index="123" title="test"
//(firefox)title="test" index="123" name="abc" id="box" class="box"
//(IE8+)title="test" class="box" id="box" index="123" name="abc"
//(IE7-)输出所有的特性
console.log(outputAttributes(document.getElementById("box")))
</script>
由上面结果看出,IE7-浏览器会返回HTML元素中所有可能的特性,包括没有指定的特性
specified
可以利用特性节点的specified属性来解决IE7-浏览器的这个问题。如果specified属性的值为true,则意味着该属性被设置过。在IE中,所有未设置过的特性的该属性值都是false。而在其他浏览器中,任何特性节点的specified值始终为true。
<div id="box" name="abc" index="123" title="test"></div>
<script>
var oBox = document.getElementById('box');
var yesItem = oBox.attributes.getNamedItem("index");
var noItem = oBox.attributes.getNamedItem("onclick");
//所有浏览器浏览器都返回true
console.log(yesItem.specified);
//IE7-浏览器返回false,而其他浏览器报错,noItem不存在
console.log(noItem.specified);
</script>
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
function outputAttributes(element){
var pairs = new Array(),attrName,attrValue,i,len;
for(i = 0,len=element.attributes.length;i<len;i++){
attrName = element.attributes[i].nodeName;
attrValue = element.attributes[i].nodeValue;
if(element.attributes[i].specified){
pairs.push(attrName +"=\"" + attrValue + "\"");
}
}
return pairs.join(" ");
}
//所有浏览器下都返回title="test" class="box" id="box" index="123" name="abc"(顺序不一样)
console.log(outputAttributes(document.getElementById("box")))
</script>
特性节点
元素特性节点attribute对应网页中HTML标签的属性,它只存在于元素的attributes属性中,并不是DOM文档树的一部分。特性节点的节点类型nodeType值是2,节点名称nodeName值是属性名,nodeValue值是属性值。
现在,div元素有id="test"的属性。
<div id="test"></div> <script> var attr = test.attributes.id; //2 'id' 'test' console.log(attr.nodeType,attr.nodeName,attr.nodeValue) console.log(Node.ATTRIBUTE_NODE === 2);//true </script>
元素的特性在DOM中以Attr类型表示,从技术角度讲,特性是存在于元素的attributes属性中的节点。尽管特性是节点,但却不是DOM节点树的一部分。
01. 特征
特性节点的三个node属性—nodeType、nodeName、nodeValue分别是2、特性名称和特性值,其父节点parentNode是null。
[注意]关于特性节点是否存在子节点,各个浏览器表现不一致。
<div id="box"></div>
<script>
var oBox = document.getElementById('box');
var oAttr = oBox.attributes;
//(chrome\safari\IE9+\firefox) 2 id box null
//(IE7-) 2 onmsanimationiteration null null
console.log(oAttr[0].nodeType,oAttr[0].nodeName,oAttr[0].value,oAttr[0].parentNode)
//(chrome\firefox) undefined
//(safari) Text
//(IE9+) box
//(IE8-) 报错
console.log(oAttr[0].childNodes[0])
</script>
02. 属性
Attr特性节点有3个属性:name、value和specified。
name
name是特性名称,与nodeName的值相同。
value
value是特性的值,与nodeValue的值相同。
specified
specified是一个布尔值,用以区别特性是在代码中指定的,还是默认的。这个属性的值如果为true,则意味着要么是在HTML中指定了相应特性,要么是通过setAttribute()方法设置了该属性。在IE中,所有未设置过的特性的该属性值都为false,而在其他浏览器中,所有设置过的特性的该属性值都是true,未设置过的特性,如果强行为其设置specified属性,则报错。因为undefied没有属性。
<div class="box" id="box"></div>
<script>
var oBox = document.getElementById('box');
var oAttr = oBox.attributes;
//(chrome\safari\IE8+)class class true
//(firefox)id id true
//(IE7-)onmsanimationiteration onmsanimationiteration true
console.log(oAttr[0].name,oAttr[0].nodeName,oAttr[0].name == oAttr[0].nodeName)
//IE7- "null" null false
//其他浏览器 box box true
console.log(oAttr[0].value,oAttr[0].nodeValue,oAttr[0].value == oAttr[0].nodeValue)
//IE7- false
//其他浏览器 true
console.log(oAttr[0].specified)//true
</script>
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
var oBox = document.getElementById('box');
console.log(oBox.attributes.id.specified)//true
console.log(oBox.attributes.onclick.specified)//在IE7-浏览器下会返回false,在其他浏览器下会报错
</script>
specified常常用于解决IE7-浏览器显示所有特性的bug。
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
function outputAttributes(element){
var pairs = new Array(),attrName,attrValue,i,len;
for(i = 0,len=element.attributes.length;i<len;i++){
attrName = element.attributes[i].nodeName;
attrValue = element.attributes[i].nodeValue;
if(element.attributes[i].specified){
pairs.push(attrName +"=\"" + attrValue + "\"");
}
}
return pairs.join(" ");
}
//所有浏览器下都返回title="test" class="box" id="box" index="123" name="abc"(顺序不一样)
console.log(outputAttributes(document.getElementById("box")))
</script>
03. 方法
createAttribute()
createAttribute()方法传入特性名称并创建新的特性节点。
setAttributeNode()
setAttributeNode()方法传入特性节点并将特性添加到元素上,无返回值。
getAttributeNode()
getAttributeNode()方法传入特性名并返回特性节点。
removeAttributeNode()
removeAttributeNode()方法传入特性名删除并返回删除的特性节点,但IE7-浏览器下无法删除。
<div id="box"></div>
<script>
var oBox = document.getElementById('box');
var attr = document.createAttribute('title');
attr.value = "test";
console.log(oBox.setAttributeNode(attr));//null
console.log(oBox.getAttributeNode("title").name,attr.name);//title title
console.log(oBox.getAttributeNode("title").value,attr.value);//test test
//返回删除的节点
console.log(oBox.removeAttributeNode(attr));
//IE7-浏览器下无法删除,其他浏览器返回null
console.log(oBox.getAttributeNode("title"));
</script>
文本节点
文本节点text代表网页中的HTML标签内容。文本节点的节点类型nodeType值是3,节点名称nodeName值是'#text',nodeValue值是标签内容值。
现在,div元素内容为'测试'。
<div id="test">测试</div> <script> var txt = test.firstChild; //3 '#text' '测试' console.log(txt.nodeType,txt.nodeName,txt.nodeValue) console.log(Node.TEXT_NODE === 3);//true </script>
文本节点顾名思义指向文本的节点,网页上看到的文字内容都属于文本节点。
元素的文本可以看成字符串,也可以看成节点;除了字符串操作方法,也可以使用正则或者文本节点方法。
01. 特征
文本节点由Text类型表示,包含的是纯文本内容,但文本节点是对象类型。
<div id="box">内容</div>
<script>
console.log(box.firstChild);//"内容"
console.log(typeof box.firstChild);//'object'
</script>
纯文本内容中的HTML字符会被转义。
<div id="box"><内容></div>
<script>
console.log(box.firstChild);//"<内容>""
</script>
文本节点的三个node属性——nodeType、nodeName、nodeValue分别是3、'#text'和节点所包含的文本,其父节点parentNode指向包含该文本节点的元素节点,文本节点没有子节点。
<div id="box">test</div> <script> var oTxt = box.firstChild; console.log(oTxt.nodeType);//3 console.log(oTxt.nodeValue);//test console.log(oTxt.nodeName);//'#text' console.log(oTxt.parentNode);//<div> console.log(oTxt.childNodes);//[] </script>
02. 空白文本节点
关于文本节点,遇到最多的兼容问题是空白文本节点问题。IE8-浏览器不识别空白文本节点,而其他浏览器会识别空白文本节点。
<div id="box">
<div>1</div>
</div>
<script>
//标准浏览器输出[text, div, text],text表示空白文本节点
//IE8-浏览器输出[div],并不包含空白文本节点
console.log(box.childNodes);
</script>
03. 属性
data
文本节点的data属性与nodeValue属性相同。
<div id="box">test</div>
<script>
var oBox = document.getElementById('box');
var oTest = oBox.firstChild;
//test test true
console.log(oTest.nodeValue,oTest.data,oTest.data === oTest.nodeValue);
</script>
wholeText
wholeText属性将当前Text节点与毗邻的Text节点,作为一个整体返回。大多数情况下,wholeText属性的返回值,与data属性和textContent属性相同。但是,某些特殊情况会有差异
[注意]IE8-浏览器不支持
<div id="test">123</div> <script> console.log(test.firstChild.wholeText);//123 console.log(test.firstChild.data);//123 //以索引1为指定位置分割为两个文本节点 test.firstChild.splitText(1); console.log(test.firstChild.wholeText);//123 console.log(test.firstChild.data);//1 </script>
length
文本节点的length属性保存着节点字符的数目,而且nodeValue.length、data.length也保存着相同的值。
<div id="box">test</div>
<script>
var oBox = document.getElementById('box');
var oTest = oBox.firstChild;
//4 4 4
console.log(oTest.length,oTest.nodeValue.length,oTest.data.length);
</script>
04. 方法
createTextNode()
createTextNode()方法用于创建文本节点,这个方法接收一个参数——要插入节点中的文本。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
var oText = document.createTextNode('<strong>hello</strong> world!');
oBox.appendChild(oText);
//'123<strong>hello</strong> world!'
console.log(oBox.innerHTML);
//此时,页面中有两个文本节点
console.log(oBox.childNodes.length);
</script>
normalize()
normalize()方法的作用是合并相邻的文本节点,该方法在文本节点的父节点——元素节点上调用。
[注意] IE9+浏览器无法正常使用该方法。
<div id="box">0</div>
<script>
var oText1 = document.createTextNode('1');
var oText2 = document.createTextNode('2');
box.appendChild(oText1);
box.appendChild(oText2);
console.log(box.childNodes);//[text, text, text]
console.log(box.childNodes.length);//3
box.normalize();
//IE9+浏览器返回[text,text],而其他浏览器返回[text]
console.log(box.childNodes);
//IE9+浏览器返回'01',而其他浏览器返回'012'
console.log(box.childNodes[0]);
//IE9+浏览器返回2,使用该方法时只能将所有的文本节点减1;其他浏览器正常,返回2
console.log(box.childNodes.length);//1
</script>
splitText()
与normalize()方法作用相反,splitText()方法将一个文本节点分成两个文本节点,即按照指定的位置分割nodeValue值。原来的文本节点将包含从开始到指定位置之前的内容。这个方法会返回一个新文本节点,包含剩下的文本。splitText()方法返回的节点与原节点的parentNode相同 。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
var newNode = oBox.firstChild.splitText(1);
console.log(newNode,newNode === oBox.lastChild);//'23' true
console.log(oBox.firstChild);//'1'
</script>
appendData()
appendData(text)方法将text添加到节点的末尾,该方法无返回值。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
var oText = oBox.firstChild;
console.log(oText.appendData('4'));//undefined
console.log(oText.data);//'1234'
console.log(oBox.childNodes.length);//1
</script>
deleteData()
deleteData(offset,count)方法从offset指定的位置开始删除count个字符,无返回值。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
var oText = oBox.firstChild;
console.log(oText.deleteData(0,2));//undefined
console.log(oText.data);//'3'
console.log(oBox.childNodes.length);//1
</script>
insertData()
insertData(offset,text)方法在offset指定的位置插入text,无返回值。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
var oText = oBox.firstChild;
console.log(oText.insertData(1,'test'));//undefined
console.log(oText.data);//'1test23'
console.log(oBox.childNodes.length);//1
</script>
replaceData()
replaceData(offset,count,text)方法用text替换从offset指定位置开始到offset+count为止的文本,无返回值。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
var oText = oBox.firstChild;
console.log(oText.replaceData(1,1,"test"));//undefined
console.log(oText.data);//'1test3'
console.log(oBox.childNodes.length);//1
</script>
substringData()
substringData(offset,count)方法提取从offset指定的位置开始到offset+count为止处的字符串,并返回该字符串。原来的文本节点无变化。
<div class="box" id="box">123</div>
<script>
var oBox = document.getElementById('box');
var oText = oBox.firstChild;
console.log(oText.substringData(1,1));//'2'
console.log(oText);//'123'
</script>
05. 性能
通过上面的方法介绍,我们会发现,文本节点的操作与字符串的操作方法相当类似。一般地,我们获取文本都用innerHTML,然后再去字符串的操作方法去操作。以下为两者的性能进行对比分析:
a. 首先,对replaceData()和replace()这两个方法进行比较。replace()方法又分为两个方法,一个是在循环中直接对innerHTML进行赋值;另一个是在循环中对变量进行赋值,最后再赋值给innerHTML。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
var oText = oBox.firstChild;
var str = oBox.innerHTML;
function stringTest1(){
for(var i = 0; i < 1000000; i++){
oBox.innerHTML = str.replace(1,Math.floor(Math.random() * 9 + 1));
}
}
var start1 = Date.now();
stringTest1();
var stop1 = Date.now();
result1 = stop1 - start1;
console.log('str1',result1)//2351
/*********************************/
function stringTest2(){
for(var i = 0; i < 1000000; i++){
str.innerHTML = str.replace(1,Math.floor(Math.random() * 9 + 1));
}
}
var start2 = Date.now();
stringTest2();
oBox.innerHTML = str;
var stop2 = Date.now();
result2 = stop2 - start2;
console.log('str2',result2)//408
/*********************************/
function dataTest1(){
for(var i = 0; i < 1000000; i++){
oText.replaceData(1,1,Math.floor(Math.random() * 9 + 1))
}
}
var start3 = Date.now();
dataTest1();
var stop3 = Date.now();
result3 = stop3 - start3;
console.log('data',result3)//327
</script>
从结果中可以看出,在100万次的循环中,直接操作innerHTML开销较大,操作文本节点的的开销最小
b. 对substring()和substringData()方法进行比较,这两种方法都用于提取子串。
<div id="box">123</div>
<script>
var oBox = document.getElementById('box');
var oText = oBox.firstChild;
var str = oBox.innerHTML;
function stringTest(){
for(var i = 0; i < 10000000; i++){
str.substring(Math.floor(Math.random() * 2),2);
}
}
var start = Date.now();
stringTest();
var stop = Date.now();
result = stop - start;
console.log('str',result)//364
/*********************************/
function dataTest(){
for(var i = 0; i < 10000000; i++){
oText.substringData(Math.floor(Math.random() * 2),1);
}
}
var start = Date.now();
dataTest();
var stop = Date.now();
result = stop - start;
console.log('str',result)//1195
</script>
从结果中可以看出,在1000万次的循环中,使用substringData()方法比substring()方法的开销较大。
CDATA节点
CDATASection类型只针对基于XML的文档,只出现在XML文档中,表示的是CDATA区域,格式一般为:
<![CDATA[ ]]>
该类型节点的节点类型nodeType的值为4,节点名称nodeName的值为'#cdata-section',nodevalue的值是CDATA区域中的内容。
实体引用名称节点
实体是一个声明,指定了在XML中取代内容或标记而使用的名称。 实体包含两个部分, 首先,必须使用实体声明将名称绑定到替换内容。 实体声明是使用 <!ENTITY name "value"> 语法在文档类型定义(DTD)或XML架构中创建的。其次,在实体声明中定义的名称随后将在 XML 中使用。 在XML中使用时,该名称称为实体引用。
实体引用名称节点entry_reference的节点类型nodeType的值为5,节点名称nodeName的值为实体引用的名称,nodeValue的值为null。
//实体名称 <!ENTITY publisher "Microsoft Press"> //实体名称引用 <pubinfo>Published by &publisher;</pubinfo>
实体名称节点
上面已经详细解释过,就不再赘述。
该节点的节点类型nodeType的值为6,节点名称nodeName的值为实体名称,nodeValue的值为null。
处理指令节点
处理指令节点ProcessingInstruction的节点类型nodeType的值为7,节点名称nodeName的值为target,nodeValue的值为entire content excluding the target。
注释节点
注释节点comment表示网页中的HTML注释。注释节点的节点类型nodeType的值为8,节点名称nodeName的值为'#comment',nodeValue的值为注释的内容。
现在,在id为myDiv的div元素中存在一个<!-- 我是注释内容 -->
<div id="myDiv"><!-- 我是注释内容 --></div> <script> var com = myDiv.firstChild; //8 '#comment' '我是注释内容' console.log(com.nodeType,com.nodeName,com.nodeValue) console.log(Node.COMMENT_NODE === 8);//true </script>
01. 特征
注释在DOM中是通过Comment类型来表示,注释节点的三个node属性——nodeType、nodeName、nodeValue分别是8、'#comment'和注释的内容,其父节点parentNode可能是Document或Element,注释节点没有子节点。
<body><!-- 我是注释--> <script> var oComment = document.body.firstChild; //#comment 8 我是注释 console.log(oComment.nodeName,oComment.nodeType,oComment.nodeValue) //<body> [] console.log(oComment.parentNode,oComment.childNodes) </script>
[注意] 所有浏览器都识别不出位于</html>后面的注释。
<!-- -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
console.log(document.childNodes.length);//3
console.log(document.firstChild,document.lastChild)//<!-- --> <html>
</script>
</body>
</html>
<!-- -->
02. 属性和方法
注释节点Comment与文本节点Text继承自相同的基类,因此它拥有除了splitText()之外的所有字符串操作方法。与Text类型相似,也可以通过nodeValue或data属性来取得注释的内容。
data
注释节点的data属性与nodeValue属性相同。
length
注释节点的length属性保存着节点字符的数目,而且nodeValue.length、data.length也保存着相同的值。
<body><!--我是注释--> <script> var oComment = document.body.firstChild; //我是注释 我是注释 true console.log(oComment.nodeValue,oComment.data,oComment.data == oComment.nodeValue); //4 4 4 console.log(oComment.length,oComment.nodeValue.length,oComment.data.length); </script> </body>
createComment()
createComment()方法用于创建注释节点,这个方法接收一个参数——要插入节点中的注释文本。
var oComment = document.createComment('hello world!');
var oBase = document.body.firstChild;
document.body.insertBefore(oComment,oBase);
//<!--hello world!-->
console.log(document.body.firstChild);
appendData()
appendData(text)方法将text添加到节点的末尾。
<body><!--我是注释-->
<script>
var oComment = document.body.firstChild;
console.log(oComment.data);//我是注释
console.log(oComment.appendData('test'));//undefined
console.log(oComment.data);//我是注释test
</script>
</body>
deleteData()
deleteData(offset,count)方法从offset指定的位置开始删除count个字符。
<body><!--我是注释--> <script> var oComment = document.body.firstChild; console.log(oComment.data);//我是注释 console.log(oComment.deleteData(0,1));//undefined console.log(oComment.data);//是注释 </script> </body>
insertData()
insertData(offset,text)方法在offset指定的位置插入text。
<body><!--我是注释--> <script> var oComment = document.body.firstChild; console.log(oComment.data);//我是注释 console.log(oComment.insertData(1,"test"));//undefined console.log(oComment.data);//我test是注释 </script> </body>
replaceData()
replaceData(offset,count,text)方法用text替换从offset指定的位置开始到offset+count处为止处的文本。
<body><!--我是注释--> <script> var oComment = document.body.firstChild; console.log(oComment.data);//我是注释 console.log(oComment.replaceData(1,1,"test"));//undefined console.log(oComment.data);//我test注释 </script> </body>
substringData()
substringData(offset,count)方法提取从offset指定的位置开始到offset+count为止处的字符串。
<body><!--我是注释--> <script> var oComment = document.body.firstChild; console.log(oComment.data);//我是注释 console.log(oComment.substringData(1,1));//是 console.log(oComment.data);//我是注释 </script> </body>
文档节点
文档节点document表示HTML文档,也称为根节点,指向document对象。文档节点的节点类型nodeType的值为9,节点名称nodeName的值为'#document',nodeValue的值为null。
<script> //9 "#document" null console.log(document.nodeType,document.nodeName,document.nodeValue) console.log(Node.DOCUMENT_NODE === 9);//true </script>
文档节点document,隶属于表示浏览器的window对象,它表示网页页面,又被称为根节点。
01. 特征
文档节点的三个node属性——nodeType、nodeValue、nodeName分别是9、'#document'和null。
由于它是根节点,所以其父节点parentNode指向null,ownerDocument也指向null。
console.log(document.nodeType);//9 console.log(document.nodeValue);//null console.log(document.nodeName);//'#document' console.log(document.parentNode);//null console.log(document.ownerDocument);//null
02. 快捷访问
子节点
【1】<html>
document.documentElement属性始终指向HTML页面中的<html>元素
console.log(document.documentElement.nodeName);//'HTML'
【2】<body>
document.body属性指向<body>元素
console.log(document.body.nodeName);//'BODY'
【3】<!DOCTYPE>
document.doctype属性指向<!DOCTYPE>标签
[注意]IE8-不识别,输出null,因为IE8-浏览器将其识别为注释节点
console.log(document.doctype.nodeName);//'html'
【4】<head>
document.head属性指向文档的<head>元素
[注意]IE8-浏览器下不支持
console.log(document.head.nodeName);//'HEAD'
文档信息
【1】title
<title>元素显示在浏览器窗口的标题栏或标签页上,document.title包含着<title>元素中的文本,这个属性可读可写
console.log(document.title);//Document document.title="test"; console.log(document.title);//test
【2】URL、domain、referrer
URL:页面的完整地址
domain:domain与URL是相互关联的,包含页面的域名
referrer:表示链接到当前页面的上一个页面的URL,在没有来源页面时,可能为空
[注意]上面这些信息都来自请求的HTTP头部,只不过可以通过这三个属性在javascript中访问它而已
console.log(document.URL);//http://www.cnblogs.com/xiaohuochai/ console.log(document.domain);//www.cnblogs.com console.log(document.referrer);//http://home.cnblogs.com/followees/
在这3个属性中,只有domain是可以设置的。但由于安全方面的限制,也并非可以给domain设罝任何值。如果URL中包含一个子域名,例如home.cnblogs.com,那么就只能将domain设置为"cnblogs.com"。不能将这个属性设置为URL中不包含的域
document.domain = 'cnblogs.com';//"cnblogs.com" //Uncaught DOMException: Failed to set the 'domain' property on 'Document': 'qq.com' is not a suffix of 'cnblogs.com' document.domain = 'qq.com';
【3】baseURI
document.baseURI返回<base>标签中的URL,如果没有设置<base>,则该值与document.URL相同
console.log(document.baseURI);'//http://www.cnblogs.com/xiaohuochai/' <base href="http://www.baidu.com"> <script> console.log(document.baseURI);//'http://www.baidu.com/' </script>
【4】字符集charset
document.charset表示文档中实际使用的字符集
console.log(document.charset);//'UTF-8'
【5】defaultView
document.defaultView保存着一个指针,指向拥有给定文档的窗口或框架。IE8-浏览器不支持defaultView属性,但IE中有一个等价的属性名叫parentWindow。所以要确定文档的归属窗口,其兼容写法为:
var parentWindow = document.defaultView || document.parentWindow;//Window
【6】兼容模式compatMode
document.compatMode表示文档的模式,在标准模式下值为"CSS1Compat",在兼容模式下值为"BackCompat"
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script> console.log(document.compatMode)//CSS1Compat </script> </body> </html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script> console.log(document.compatMode)//BackCompat </script> </body> </html>
【7】文档模式documentMode
document.documentMode属性表示当前的文档模式
[注意]该属性只有IE11-浏览器支持
//IE11返回11,IE10返回10,IE9返回9,IE8返回8,IE7返回7,IE6返回6 console.log(document.documentMode);
【8】时间戳lastModified
document.lastModified属性返回当前文档最后修改的时间戳,格式为字符串
console.log(document.lastModified); //09/02/2016 15:36:15
节点集合
【1】anchors
document.anchors包含文档中所有带name特性的<a>元素。
<a href= "#" name="a1">a1</a> <a href= "#" name="a2">a2</a> <a href= "#" >3</a> <script> console.log(document.anchors.length)//2 </script>
【2】links
document.links包含文档中所有带href特性的<a>元素。
<a href="#">1</a> <a href="#">2</a> <a>3</a> <script> console.log(document.links.length)//2 </script>
【3】forms
document.forms包含文档中所有的<form>元素,与document.getElementsByTagName("form")结果相同。
<form action="#">1</form> <form action="#">2</form> <script> console.log(document.forms.length)//2 </script>
【4】images
document.images包含文档中所有的<img>元素,与document.getElementsByTagName('img')结果相同。
<img src="#" alt="#"> <img src="#" alt="#"> <script> console.log(document.images.length)//2 </script>
【5】scripts
document.scripts属性返回当前文档的所有脚本(即script标签)。
<script> console.log(document.scripts.length)//1 </script>
以上五个属性返回的都是HTMLCollection对象实例
console.log(document.links instanceof HTMLCollection); // true console.log(document.images instanceof HTMLCollection); // true console.log(document.forms instanceof HTMLCollection); // true console.log(document.anchors instanceof HTMLCollection); // true console.log(document.scripts instanceof HTMLCollection); // true
由于HTMLCollection实例可以用HTML元素的id或name属性引用,因此如果一个元素有id或name属性,就可以在上面这五个属性上引用。
<form name="myForm"> <script> console.log(document.myForm === document.forms.myForm); // true </script>
03. 文档写入方法
将输出流写入到网页的能力有4个方法:write()、writeln()、open()、close()。
write()和writeln()
write()和writeln()方法都接收一个字符串参数,即要写入到输出流中的文本。
write()会原样写入,而writeln()则在字符串的末尾添加一个换行符(\n),但换行符会被页面解析为空格。
在页面被加载的过程中,可以使用这两个方法向页面中动态地加入内容。
<button id="btn">替换内容</button>
<script>
btn.onclick = function(){
document.write('123');
document.writeln('abc');
document.write('456');
}
</script>
open()和close()
open()和close()方法分别用于打开和关闭网页的输出流。
open()方法实际上等于清除当前文档。
<button id="btn">清除内容</button>
<script>
btn.onclick = function(){
document.open();
}
</script>
close()方法用于关闭open方法所新建的文档。一旦关闭,write方法就无法写入内容了。如果再调用write方法,就等同于又调用open方法,新建一个文档,再写入内容。所以,实际上,close()只是和open()方法配套使用而已。
<button id="btn">替换内容</button>
<script>
//相当于'123'又把'1'覆盖了
btn.onclick = function(){
document.open();
document.write('1');
document.close();
document.write('123');
}
</script>
一般地,先使用open()方法用于新建一个文档,然后使用write()和writeln()方法写入文档,最后使用close()方法,停止写入。
<button id="btn">替换内容</button>
<script>
btn.onclick = function(){
document.open();
document.writeln('hello');
document.write('world');
document.close();
}
</script>
[注意] 如果是在页面加载期间使用write()和writeln()方法,则不需要用到这两个方法。
<button id="btn">内容</button>
<script>
document.writeln('hello');
document.write('world');
</script>
文档类型节点
文档类型节点DocumentType包含着与文档的doctype有关的所有信息。文档类型节点的节点类型nodeType的值为10,节点名称nodeName的值为doctype的名称,nodeValue的值为null。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script> var nodeDocumentType = document.firstChild; //10 "html" null console.log(nodeDocumentType.nodeType,nodeDocumentType.nodeName,nodeDocumentType.nodeValue); console.log(Node.DOCUMENT_TYPE_NODE === 10); </script> </body> </html>
01. 特征
文档类型节点DocumentType的三个node属性——nodeType、nodeName、nodeValue分别是10、doctype的名称和null,其父节点parentNode是Document,文档类型节点没有子节点。
文档类型节点有一个快捷写法是document.doctype,但是该写法IE8-浏览器不支持。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
//IE8-浏览器不支持document.doctype
var oDoctype = document.doctype;
if(oDoctype){
// html 10 null
console.log(oDoctype.nodeName,oDoctype.nodeType,oDoctype.nodeValue);
//#document []
console.log(oDoctype.parentNode,oDoctype.childNodes)
}
</script>
</body>
</html>
02. 属性
文档类型节点DocumentType对象有3个属性:name、entities、notations
name
name表示文档类型的名称,与nodeName的属性相同
entities
entities表示由文档类型描述的实体的NamedNodeMap对象
notations
notations表示由文档类型描述的符号的NamedNodeMap对象
通常浏览器中的文档使用的都是HTML或XHTML文档类型,因而entites和notations都是空列表(列表中的项来自行内文档类型声明)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
//IE8-浏览器不支持document.doctype
var oDoctype = document.doctype;
if(oDoctype){
console.log(oDoctype.name,oDoctype.name=== oDoctype.nodeName);//html true
console.log(oDoctype.entities,oDoctype.notations);//undefined undefined
}
</script>
</body>
</html>
【IE8-Bug】
IE8-浏览器将标签名为"!"的元素视作注释节点,所以文档声明也被视作注释节点。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
var oDoctype = document.firstChild;
//IE8-浏览器返回8,其他浏览器返回10
console.log(oDoctype.nodeType);
</script>
</body>
</html>
文档片段节点
文档片段节点DocumentFragment在文档中没有对应的标记,是一种轻量级的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。该节点的节点类型nodeType的值为11,节点名称nodeName的值为'#document-fragment',nodeValue的值为null。
<script> var nodeDocumentFragment = document.createDocumentFragment(); //11 "#document-fragment" null console.log(nodeDocumentFragment.nodeType,nodeDocumentFragment.nodeName,nodeDocumentFragment.nodeValue); console.log(Node.DOCUMENT_FRAGMENT_NODE === 11);//true </script>
在所有节点类型中,只有文档片段节点DocumentFragment在文档中没有对应的标记。DOM规定文档片段(document fragment)是一种“轻量级”的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。
由于文档片段的优点在IE浏览器下并不明显,反而可能成为多此一举。所以,该类型的节点并不常用。
01. 特征
创建文档片段,要使用document.createDocumentFragment()方法。文档片段继承了Node的所有方法,通常用于执行那些针对文档的DOM操作。
文档片段节点的三个node属性——nodeType、nodeName、nodeValue分别是11、'#document-fragment'和null,文档片段节点没有父节点parentNode。
var frag = document.createDocumentFragment(); console.log(frag.nodeType); // 11 console.log(frag.nodeValue); // null console.log(frag.nodeName); // '#document-fragment' console.log(frag.parentNode); // null
02. 作用
我们经常使用javascript来操作DOM元素,比如使用appendChild()方法。每次调用该方法时,浏览器都会重新渲染页面。如果大量的更新DOM节点,则会非常消耗性能,影响用户体验。
javascript提供了一个文档片段DocumentFragment的机制。如果将文档中的节点添加到文档片段中,就会从文档树中移除该节点。把所有要构造的节点都放在文档片段中执行,这样可以不影响文档树,也就不会造成页面渲染。当节点都构造完成后,再将文档片段对象添加到页面中,这时所有的节点都会一次性渲染出来,这样就能减少浏览器负担,提高页面渲染速度。
<ul id="list1"></ul>
<script>
var list1 = document.getElementById('list1');
console.time("time");
var fragment = document.createDocumentFragment();
for(var i = 0; i < 500000; i++){
fragment.appendChild(document.createElement('li'));
}
list1.appendChild(fragment);
console.timeEnd('time');
</script>
<ul id="list"></ul>
<script>
var list = document.getElementById('list');
console.time("time");
for(var i = 0; i < 500000; i++){
list.appendChild(document.createElement('li'));
}
console.timeEnd('time');
</script>
循环50万次的各浏览器结果
使用文档片段 不使用文档片段 firefox 402.04ms 469.31ms chrome 429.800ms 729.634ms
循环10万次的各浏览器结果
使用文档片段 不使用文档片段 IE11 2382.15ms 2204.47ms IE10 2404.239ms 2225.721ms IE9 2373ms 2255ms IE8 4464ms 4210ms IE7 5887ms 5394ms
由以上结果可以看出,若使用IE浏览器,则使用文档片段DocumentFragment的性能并不会更好,反而变差;若使用chrome和firefox浏览器,使用文档片段DocumentFragment可以提升性能。
DTD声明节点
DTD声明节点notation代表DTD中声明的符号。该节点的节点类型nodeType的值为12,节点名称nodeName的值为符号名称,nodeValue的值为null。