HTML可以描绘成一个多层节点构成的结构,文档节点是每个文档的根节点,<html>元素被称之为文档元素,是文档的最外层元素。不同的标记用不同的节点来表示。
1. Node类型
Node接口在Javascript中是作为Node类型实现的,除了IE之外,在其他所有浏览器中都可以访问到这个类型。而Javascript的所有节点类型都继承自Node类型。
-
1.1nodeType属性
表明节点类型:1.Node.ELEMENT_NODE,2.Node.ATTRIBUTE_NODE,3.Node.TEXT_NODE,4.Node.CDATA_SECTION_NODE,5.Node.ENTITY_REFERENCE_NODE,6.Node.ENTITY_NODE,7.Node.PROCESSING_INSTRUCTION_NODE,8.Node.COMMENT_NODE,9.Node.DOCUMENT_NODE,10.Node.DOCUMENT_TYPE_NODE,11.Node.DOCUMENT_FRAGMENT_NODE,12.Node.NOTATION_NODE
var btn1 = document.getElementsByClassName('btn1')[0];
if(btn1.nodeType == 1){
console.log('这是元素节点');
}
-
1.2nodeName 和 nodeValue 属性
//对于元素节点,nodeName中保存的始终都是元素的标签名,而nodeValue的值则始终为null
console.log(btn1.nodeName);//这里是一个按钮,所以打印BUTTON
console.log(btn1.nodeValue);
-
1.3节点关系
- childNodes:NodeList 对象,NodeList 对象是类数组对象,用于保存一组有序的节点。
html:
<ul class="ul-list"><li>第一行</li><li>第二行</li><li>第三行</li></ul>
js:
var ulList = document.getElementsByClassName('ul-list')[0];
console.log(ulList.childNodes);
var first = ulList.childNodes[0];
console.log(first);
var last = ulList.childNodes[ulList.childNodes.length-1];
console.log(last);
//hasChildNodes()方法判断是否有子节点
console.log(ulList.hasChildNodes());
//ownerDocument返回所在的文档,而且每个节点只能在一个文档中
console.log(ulList.ownerDocument);
- parentNode:父节点
//打印ulList
console.log(last.parentNode);
- previousSibling和nextSibling
console.log(second.previousSibling);//打印第三行
console.log(second.nextSibling);//打印第一行
console.log(last.nextSibling);//打印null
- firstChild和lastChild
console.log(ulList.firstChild);//打印第一行
console.log(ulList.lastChild);//打印第三行
-
1.4操作节点
- appendChild()
html:
<div class="div-con">
<p>div中的第一行</p>
<p class="second-p">div中的第二行</p>
<p>div中的第三行</p>
</div>
js:
var div1 = document.getElementsByClassName('div-con')[0];
var p = document.createElement("p");
p.innerText = '这是新创建的段落';
//返回值是被添加的子节点
var appendP = div1.appendChild(p);
console.log(appendP == p);
//如果传入到 appendChild()中的节点已经是文档的一部分,那么就是将该节点从原来的位置转移到新位置
var secondP = document.getElementsByClassName('second-p')[0];
div1.appendChild(secondP);
- insertBefore()
var div1 = document.getElementsByClassName('div-con')[0];
var secondP = document.getElementsByClassName('second-p')[0];
var p = document.createElement("p");
p.innerText = '这是新创建的段落';
//insertBefore:指定放到某个子节点之前,如果第二个参数传null,则新节点会被放在最后
div1.insertBefore(p,secondP);
- replaceChild()
var div1 = document.getElementsByClassName('div-con')[0];
var secondP = document.getElementsByClassName('second-p')[0];
var p = document.createElement("p");
p.innerText = '这是新创建的段落';
//replaceChild:替换子节点
div1.replaceChild(p,secondP);
- removeChild()
var div1 = document.getElementsByClassName('div-con')[0];
var secondP = document.getElementsByClassName('second-p')[0];
//removeChild:删除子节点
div1.removeChild(secondP);
-
1.5其他方法
- cloneNode()
var ulList = document.getElementsByClassName('ul-list')[0];
var div1 = document.getElementsByClassName('div-con')[0];
//cloneNode:传是否深复制的参数,true为深复制(把子节点也复制过来),false则否.
//事件处理程序不会被复制,但IE有bug,会复制事件处理程序,所以复制之前最好把事情处理程序去掉
var cloneList = ulList.cloneNode(true);
console.log(cloneList);
//注意的是,cloneNode完之后必须要为它指定父节点,不然它无法显示
div1.appendChild(cloneList);
- normalize
这个方法唯一的作用就是处理文档树中的文本节点。 由于解析器的实现或 DOM 操作等原因,可能会出现文本节点不包含文本,或者接连出现两个文本节点 的情况。当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述两种情况。如果找到了 空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并为一个文本节点。
2.Document类型
在浏览器中,document对象是window的属性,可以直接访问.而document对象是HTMLDocument类型的实例(HTMLDocument类型继承自Document类型),document对象表示整个文档。
-
2.1文档的子节点
console.log(document);
//为9
console.log(document.nodeType);
//为:#document
console.log(document.nodeName);
//为null
console.log(document.nodeValue);
//为null
console.log(document.parentNode);
//为null
console.log(document.ownerDocument);
//打印子节点:子节点可能有DocumentType,Element,ProcessingIn-struction,Comment.
//兼容性的问题是不同浏览器对于出现在<html></html>以外的注释Comment是否加入到document的子节点中不一致
console.log(document.childNodes);
var docuChild = document.childNodes;
for(var i=0,len=docuChild.length;i<len;i++){
console.log(docuChild[i].nodeType);
}
//各浏览器对于doctype的支持不一致
console.log(document.doctype);
//常用的话是document的以下属性:
//documentElement指向<html>元素
console.log(document.documentElement);
//指向<body>元素
console.log(document.body);
-
2.2文档信息
//有一些属性是Document类型没有,是HTMLDocument类型才有的属性:
//标题
console.log(document.title);
//可以修改文档标题
document.title = 'bom学习';
//URL:完整URL地址,domain:域名,referrer:来源页面的URL(如果没有就是空字符串)
console.log(document.URL);
console.log(document.domain);
console.log(document.referrer);
//可以设置domain:当页面中包含来自其他子域的框架或内嵌框架时,能够设置document.domain就非常方便了。
//由于跨域安全限制,来自不同子域的页面无法通过 JavaScript 通信。
//而通过将每个页面的 document.domain 设置为相同的值,
//这些页面就可以互相访问对方包含的 JavaScript 对象了
//不能将这个属性设置为URL中不包含的域
//假设页面来自 p2p.wrox.com 域
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出错!
//浏览器对domain属性还有一个限制,即如果域名一开始是“松散的”(loose),那么不能将它再设置为“紧绷的”(tight)
//假设页面来自于 p2p.wrox.com 域
document.domain = "wrox.com"; //松散的(成功)
document.domain = "p2p.wrox.com"; //紧绷的(出错!)
-
2.3查找元素
- getElementById():返回文档中指定id特性的元素,只返回文档中第一次出现指定id的元素。兼容性:①IE8 及较低版本不区分 ID 的大小写,其他浏览器都会区分;②IE7及更低版本:name 特性与给定 ID 匹配的表单元素(<input>、 <textarea>、<button>及<select>)也会被该方法返回(所以要避免name跟id相同的值)。
- getElementsByTagName():返回的是包含零或多个元素的 NodeList,在 HTML 文档中,这个方法会返回一个 HTMLCollection 对象。
//将HTMLCollection对象保存在lis变量中
var imgs = document.getElementsByTagName('img');
console.log(imgs);
//获取其中某个元素:①item()方法
console.log(imgs.item(0));
//②类似数组传个index:底层调用了item()方法
console.log(imgs[0]);
//③使用这个方法可以通过元素的name特性取得集合中的项
console.log(imgs.namedItem('shop'));
//④HTMLCollection 还支持按名称访问项:底层调用了namedItem()方法
console.log(imgs["changephone"]);
//如果传入*当做tagName的话,会返回所有的元素
var allEles = document.getElementsByTagName("*");
- getElementsByName():HTMLDocument类型才有的方法.
这个方法会返回带有给定 name 特性的所有元素,也是HTMLCollectioin集合.
html:
<div>
<input type="radio" name="fruit" id="apple" value="苹果">
<label for="apple">苹果</label>
<input type="radio" name="fruit" id="pear" value="雪梨">
<label for="pear">雪梨</label>
<input type="radio" name="fruit" id="banana" value="香蕉">
<label for="banana">香蕉</label>
</div>
js:
var radios = document.getElementsByName('fruit');
console.log(radios);
-
2.4特殊集合
除了属性和方法,document 对象还有一些特殊的集合。这些集合都是 HTMLCollection 对象, 为访问文档常用的部分提供了快捷方式:
html:
<a>百度</a>
<a href="http://piaoshu.org" name="piaoshu">漂书</a>
<a href="http://taobao.com">淘宝</a>
js:
//包含文档中所有的<form>元素
console.log(document.forms);
//包含文档中所有的<img>元素
console.log(document.images);
//包含文档中所有带有name特性的<a>元素
console.log(document.anchors);//1个
//包含文档中所有带 href 特性的<a>元素
console.log(document.links);//2个
//包含文档中所有的<a>元素
console.log(document.getElementsByTagName('a'));//3个
-
2.5DOM一致性检测
DOM分为多个级别,也包含多个部分,所以一致性检测有需要:
//参数:要检测的 DOM 功能的名称及版本号,如果实现了就返回true
console.log(document.implementation.hasFeature('HTML','2.0'));
Snip20180125_31.png
但hasFeature的返回有时会不准确(浏览器可能是返回了true但并未全部实现),所以大多数情况下推荐使用能力检测.
-
2.6文档写入
//文档写入:writeln和write的区别是writeln在写入之后加了一个'\n'符
//还可以写入script文件等等,注意对"和/script的转义
document.write("<strong>" + (new Date()).toLocaleString() + "</strong>");
document.writeln("<img src='../../weixin/images/category@2x.png'>");
document.write("<script type=\"text/javascript\" src=\"dom1.js\"><\/script>");
//如果onload之中写入会覆盖文档的原有内容
window.onload = function () {
document.write('覆盖性写入');
}
//方法 open()和 close()分别用于打开和关闭网页的输出流
3.Element类型
var btn1 = document.getElementsByClassName('btn1')[0];
//为1
console.log(btn1.nodeType);
//为null
console.log(btn1.nodeValue);
//BUTTON(是大写)
console.log(btn1.nodeName);
console.log(btn1.tagName);
//要比较标签名的话直接转化为小写比较会有通用性(HTML和XML中)
console.log(btn1.tagName.toLowerCase() == 'button');
-
3.1 HTML元素
HTML元素:用HTMLElement类型表示,不是直接通过这个类型,也是通过它的子类型来表示,如HTMLDivElement,HTMLButtonElement,HTMLImageElement等等.HTMLElement直接继承自Element类型并添加了一些属性:①id:元素在文档中的唯一标识符,②className:与元素的class特性对应,③title:有关元素的附加说明信息,一般通过工具提示条显示出来,④lang:元素内容的语言代码,很少使用,⑤dir:语言的方向,ltr左到右,rtl右到左,很少使用.
获取属性:
var btn1 = document.getElementById('btn1');
console.log(btn1.id);
console.log(btn1.className);
console.log(btn1.title);
console.log(btn1.lang);
console.log(btn1.dir);
console.log(btn1.onclick);
console.log(btn1.style);
设置属性:
btn1.id = 'newBtn1';
btn1.title = '设置的title属性';
-
3.2取得特性
getAttribute()方法:参数是特性名称.而特性并不等于属性,这里做一个分类:
- ①:公认特性(标准特性):就是指上面所述的.标准特性的特点:BOM对象会为标准特性创建相应名称的属性(class例外,对应属性名称是className,因为class是关键词).访问属性和特性获取到的内容一样,有两个特殊:onclick和style,属性返回对象,特性返回字符串(IE7及更早版本除外,特性也返回对象)
var btn1 = document.getElementById('btn1');
console.log(btn1.getAttribute('id'));
console.log(btn1.getAttribute('title'));
console.log(btn1.getAttribute('class'));
console.log(btn1.getAttribute('onclick'));
console.log(btn1.getAttribute('style'));
- 自定义特性(特性名称不区分大小写)(自定义特性一般加data-前缀):对应自定义特性,只有IE才会把它以属性的形式添加到DOM对象,其他浏览器则不会,一般使用getAttribute()获取
console.log(btn1.getAttribute('data-index'));
所以总结起来就是:以编程的方式操纵DOM时,常使用对象的属性,对于自定义特性才使用getAttribute().
-
3.3设置特性
//设置特性:
btn1.setAttribute('id','myBtn1');
console.log(btn1.id);
btn1.setAttribute('data-index','40');
console.log(btn1.getAttribute('data-index'));
//建议:除了设置自定义特性以外,都建议通过设置属性来设置特性,方便明了.
removeAttribute():用于彻底删除元素的特性,调用这个方 法不仅会清除特性的值,而且也会从元素中完全删除特性,这个方法并不常用,但在序列化 DOM 元素时,可以通过它来确切地指定要包含哪些特性。
-
3.4属性attributes
attributes属性:Element类型是使用attributes属性的唯一一个DOM节点类型,attributes属性中包含一个NamedNodeMap,与NodeList类似,也是一个“动态”的集合,元素的每一个特性都由一个Attr节点表示,每个节点都保存在NamedNodeMap对象中.
var btn1 = document.getElementById('btn1'),attrs = btn1.attributes;
console.log(attrs);
for(var i=0,len=attrs.length;i<len;i++){
console.log(attrs[i].nodeType);
console.log(attrs[i].nodeValue);
}
attributes作为NamedNodeMap,有以下方法:①getNamedItem:返回特定nodeName的节点,②removeNamedItem:移除特定nodeName的节点,③setNamedItem(node):向列表中添加节点,④item(pos):返回位于数字pos位置的节点.
console.log(attrs.getNamedItem('class'));
console.log(attrs.getNamedItem('data-index'));
console.log(attrs.item(3));
//方括号访问:
console.log(attrs["id"]);
//赋值
attrs["id"].nodeValue = 'myBtn';
一般取特性,给特性赋值都很少使用attributes,直接使用getAttribute(),setAttribute()更方便,一般用于遍历特性:
function outputAttributes(element) {
var pairs = [],
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(" ");
}
说明:每个特性节点都有一个名为specified的属性,这个属性的值如果 为 true,
则意味着要么是在HTML中指定了相应特性,
要么是通过setAttribute()方法设置了该特性.
这里针对的是IE7及更早版本的问题:IE7及更早的版本会返回HTML元 素中所有可能的特性,
包括没有指定的特性.
-
3.5创建元素
//创建元素
var div = document.createElement("div");
console.log(div.ownerDocument == document);
//在IE中可以以另一种方式使用createElement(),即为这个方法传入完整的元素标签,也可以包含属性:
var div1 = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div >");
//这种方式有助于避开在 IE7 及更早版本中动态创建元素的某些问题(在此不做列举),其他浏览器都不支持这种用法
-
3.6元素的子节点
元素的子节点:子节点有可能是元素、文本节点、注释或处理指令.不同浏览器对于看待子节点有不同:
html:
<ul class="ul-list">
<li>第一行</li>
<li>第二行</li>
<li>第三行</li>
</ul>
js:
var ulList = document.getElementsByClassName('ul-list')[0];
console.log(ulList.childNodes);
如果是 IE 来解析这些代码,ul有3个子节点,其他浏览器解析的话有7个,包括了4个text节点.如果将元素间的空白符删除,将返回相同数量的子节点.
元素也支持 getElementsByTagName() 方法,探索的起点是当前元素.
4.Text类型
nodeType是3,nodeName是 #text ,nodeValue/data是节点所包含的文本:
var text = document.getElementsByClassName('text-con')[0].firstChild;
console.log(text.nodeType);
console.log(text.nodeName);
console.log(text.nodeValue);
操作方法:除了以下的,还有splitText(),substringData():
text.nodeValue = "修改的文本";
console.log(text.nodeValue);
text.appendData('我是新增加进来的文本');
text.insertData(3,'插入的文本');
text.deleteData(0,3);
text.replaceData(0,3,'我是替代进来的文本');
console.log(text.length);
-
4.1创建文本节点
var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("我是文本");
element.appendChild(textNode);
var anotherTextNode = document.createTextNode("我是另外的文本");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
//这种情况下两个节点中的文本就会连起来显示,中间不会有空格,因为两个文本节点是同胞节点.
-
4.2规范化文本节点
规范化就是把相邻的文本节点合并成一个文本节点:
接上:
console.log(element.childNodes);//打印2个文本节点
element.normalize();//规范化
console.log(element.childNodes);//打印1个文本节点
-
4.3分割文本节点
在指定位置把一个文本节点分割开2个文本节点:
element.firstChild.splitText(5);
//第一个是"我是文本我"(从0开始,5之前结束),第二个是"是另外的文本"
console.log(element.childNodes);
5.Comment类型
Comment即注释:nodeType为8,nodeName为#comment,nodeValue/data是注释的内容.Comment 类型与 Text 类型继承自相同的基类,因此它拥有除 splitText()之外的所有字符串操作方法:
var div = document.getElementsByClassName('div-con')[0];
div.append(document.createComment('我是DOM添加的注释内容啊'));
6.CDATASection类型
CDATASection 类型只针对基于 XML 的文档,表示的是 CDATA 区域。与 Comment 类似, CDATASection 类型继承自 Text 类型,因此拥有除 splitText()之外的所有字符串操作方法。
- nodeType为4
- nodeName为#cdata-section
- nodeValue是是 CDATA 区域中的内容
7.DocumentType类型
DocumentType 类型在 Web 浏览器中并不常用,仅有 Firefox、Safari 和 Opera 和部分chrome支持它.
- nodeType为0
- nodeName的值为 doctype 的名称
- nodeValue 的值为 null
- parentNode 是 Document
在 DOM1 级中,DocumentType 对象不能动态创建,而只能通过解析文档代码的方式来创建。支持它的浏览器会把 DocumentType 对象保存在 document.doctype 中。
DOM1 级描述了 DocumentType 对象的 3 个属性:name、entities 和 notations。其中,name 表示文档类型的名称; entities 是由文档类型描述的实体的 NamedNodeMap 对象;notations 是由文档类型描述的符号的 NamedNodeMap 对象。通常,浏览器中的文档使用的都是 HTML 或 XHTML 文档类型,因而 entities 和 notations 都是空列表(列表中的项来自行内文档类型声明).
所以,只有 name 属性是有用的。这个属性中保存的是文档类型的名称,也就是出现在<!DOCTYPE 之后的文本。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
//DocumentType 的 name 属性中保存的就是html
IE 及更早版本不支持 DocumentType,因此 document.doctype 的值始终都等于 null。可是, 这些浏览器会把文档类型声明错误地解释为注释,并且为它创建一个注释节点。IE9 会给 document.doctype 赋正确的对象,但仍然不支持访问 DocumentType 类型。
8.DocumentFragment类型
在所有节点类型中,只有 DocumentFragment 在文档中没有对应的标记。DOM 规定文档片段是一种“轻量级”的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。
- nodeType为11
- nodeName 的值为"#document-fragment"
- nodeValue为null
- parentNode为null
- 子节点可以是Element、ProcessingInstruction、Comment、Text、CDATASection 或EntityReference.
文档片段继承了 Node 的所有方法,通常用于执行那些针对文档的 DOM 操作。常用document fragment当做仓库使用,避免多次渲染和布局文档:
var fragment = document.createDocumentFragment(),ul = document.getElementById('temp-ul');
var li = null;
for(var i=0;i<3;i++){
li = document.createElement('li');
li.append(document.createTextNode('我是新创建的第'+i+'个li'));
fragment.append(li);
}
ul.append(fragment);
9.Attr类型
特性节点就是存在于元素的 attributes 属性中的节点:
- nodeType是2
- nodeName 的值是特性的名称
- nodeValue 的值是特性的值
- parentNode 的值为 null
- 在 HTML 中不支持(没有)子节点,在 XML 中子节点可以是 Text 或 EntityReference
尽管它们也是节点,但特性却不被认为是 DOM 文档树的一部分。我们最常使用的是 getAttribute()、setAttribute()和 remveAttribute()方法,很少直接引用特性节点。
Attr 对象有 3 个属性:name、value 和 specified。其中,name 是特性名称(与nodeName的值相同),value 是特性的值(与nodeValue的值相同),而 specified是一个布尔值,用以区别特性是在代码中指定的,还是默认的。
创建Attr和赋给元素使用:document.createAttribute和ele.setAttributeNode.
网友评论