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 浏览器中并不常用, 仅有 FirefoxSafari 和 Opera 和部分 chrome 支持它.
nodeType 为 0
nodeName 的值为 doctype 的名称
nodeValue 的值为 null
parentNode 是 Document
在 DOM1 级中, DocumentType 对象不能动态创建, 而只能通过解析文档代码的方式来创建支持它的浏览器会把 DocumentType 对象保存在 document.doctype 中
DOM1 级描述了 DocumentType 对象的 3 个属性: nameentities 和 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
子节点可以是 ElementProcessingInstructionCommentTextCDATASection 或 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 个属性: namevalue 和 specified 其中, name 是特性名称(与 nodeName 的值相同),value 是特性的值(与 nodeValue 的值相同), 而 specified 是一个布尔值, 用以区别特性是在代码中指定的, 还是默认的
创建 Attr 和赋给元素使用: document.createAttribute 和 ele.setAttributeNode.
来源: http://www.jianshu.com/p/c70e73c97a45