节点层次

DOM可以将任何HTML文档描绘成一个由多节点构成的结构。有12种节点类型,这些类型都继承自一个基类。

Node类型

node是一个接口,所有DOM类型实现这个接口。JavaScript 中的所有节点类型都继承自 Node 类型,因此所有节点类型都共享着相同的基本属性和方法。 最常用元素和文本节点。
nodeType属性,只读

返回一个与该节点类型对应的无符号短整型的值

ELEMENT_NODE     1
ATTRIBUTE_NODE   2
TEXT_NODE        3
...

nodeName, nodeValue属性
nodeName的值取决于node的类型,元素节点的值为标签名,Text node值为’#text’字符串,Document node值为’#document’字符串
nodeValue 属性返回或设置当前节点的值。对于文档节点和元素节点来说, nodeValue返回null. 对于text, comment, 和 CDATA 节点来说, nodeValue返回该节点的文本内容. 对于 attribute 节点来说, 返回该属性的属性值.

childNodes
每个元素有childNodes属性,返回包含指定节点的子节点的集合(NodeList类型,包括元素节点,文本节点等)

NodeList
NodeList 对象是一个节点的集合,是动态实时变化的。
不是数组,但是可以使用方括号语法。

将 NodeList 转换为 Array
使用 Array.prototype.slice.call()方法将NodeList对象转化为数组。

var div_list = document.querySelectorAll('div'); // 返回 NodeList
var div_array = Array.prototype.slice.call(div_list); // 将 NodeList 转换为数组

parentNode返回指定的节点在DOM树中的父节点

appendChild() 方法将一个节点添加到指定父节点的子节点列表末尾。


var newNode = document.createElement("h1");
var text = document.createTextNode("newNode");
newNode.appendChild(text);

insertBefore()方法在参考节点之前插入一个节点作为一个指定父节点的子节点

var insertedElement = parentElement.insertBefore(newElement, referenceElement);

如果referenceElement为null则newElement将被插入到子节点的末尾。如果newElement已经在DOM树中,newElement首先会从DOM树中移除。

insertedElement 是被插入的节点,即 newElement
parentElement 是新插入节点的父节点
newElement 是被插入的节点
referenceElement 在插入newElement之前的那个节点

replaceChild()

removeChild()

/** <div class="out"> <p>p0</p> <p>p1</p> </div> */
var div = document.querySelector(".out");
var e = div.insertBefore(p[1], p[0]);
div.replaceChild(newNode, p[1]);
div.removeChild(newNode);

cloneNode()
方法返回调用该方法的节点的一个副本.接收一个布尔类型的参数,参数的默认值为 true。放回一个节点副本(未指定父节点)。
true代表深复制,false代表浅复制。深复制,复制整个节点包括这个节点的子节点。浅复制只复制这个节点。

/** <ul class="my-list"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> </ul> */
var myList = document.querySelector(".my-list");
var deepClone = myList.cloneNode(true);
var shallowClone = myList.cloneNode(false);
console.log("deep:"+deepClone.children.length);//deep:4
console.log("shallow:"+shallowClone.children.length);//shallow:0

Document类型

JavaScript通过Doucument类型表示文档。window 对象表示一个包含DOM文档的窗口,其 document 属性指向窗口中载入的 DOM文档 。浏览器中的document对象是HTMLDocument的一个实例。
Document.documentElement是一个会返回文档对象(document)的根元素的只读属性(如HTML文档的 元素)
Document.body返回当前文档中的元素或者元素.document.body是包含当前页面内容的元素,对于拥有<body>元素的文档来说,返回的是<body>元素,对于一个拥有<frameset>元素的文档来说,返回的是最外层的<frameset>元素
Document.doctype是一个只读属性, 返回的对象实现了 DocumentType 接口;()

文档信息
document.返回当前文档的URL地址
document.domain包含页面的域名
document.rederrer返回链接当前页面的那个页面的URI。

console.log(document.URL);
console.log(document.domain);
console.log("referrer:"+document.referrer);

// output:
// http://jsrun.net/run/nMgKp/display/
// jsrun.net
// referrer:http://jsrun.net/nMgKp/edit

查找元素
document.getElementById()
document.getElementsByTagName(),返回HTMLCollection集合

最新的 W3C 规范 说明这些元素是 HTMLCollection(HTML集合); 
然而, 这个方法在WebKit内核的浏览器中返回一个 NodeList 。

HTMLCollection是元素集合,它和NodeList很像,有length属性来表示HTMLCollection对象的长度,也可以通过elements.item()传入元素索引来访问。当时它还有一个nameItem()方法,可以返回集合中name属性和id属性值得元素。

Element类型

访问元素标签名,可以用nodeName也可以用tagName

//在HTML中,标签名都以大写表示
console.log("tagName:"+myList.tagName);// tagName:UL
console.log("nodeName:"+myList.nodeName);//> tagName:UL

HTML元素
HTMLElement 继承自父接口Element。HTMLElement 接口表示所有的 HTML 元素。一些HTML元素直接实现了HTMLElement接口,其它的间接实现HTMLElement接口。
attribute是HTML标签上的特性,它的值只能够是字符串,不区分大小写;

/** 访问或修改HTML元素的属性(attribute特性) <div id="myDiv" class="div" title="haha"></div> */
var myDiv = document.getElementById("myDiv");
myDiv.id="myDiv2";
console.log(myDiv.id);//myDiv2
console.log(myDiv.className);//div
console.log(myDiv.title);//haha

getAttribute()
取得属性(attribute特性),传入的属性名与实际名一致

/** <div id="myDiv" class="div" title="haha" data-attr="special" ></div> */
console.log(myDiv.getAttribute("class"));//div
//getAttribute()也可以取得自定义属性(attribute特性),自定义属性应该加上data-前缀以便验证
console.log(myDiv.getAttribute("data-attr"));//special

property
是DOM中的属性,是JavaScript里的对象;
DOM对象中的property与HTML元素的attribute(非自定义)一一对应(除了IE7及之前,IE对自定义attribute也创建property)

两类特殊的attribute通过对应的property获得的值与getAttribute()获得的值不同。
1.stylegetAttribute()返回css文本,通过property访问,返回一个对象。
2.事件属性,eg.onclickgetAttribute()返回相应代码的字符串,property返回一个JavaScript函数(或null)。

setAttribute()设置attribute,自动变小写

myDiv.setAttribute("attr1", "attr1");
//这样添加的property,不会成为HTMl元素的attribute
myDiv.attr2 = "attr2";
console.log(myDiv.getAttribute("attr1"));//attr1
console.log(myDiv.getAttribute("attr2"));//
console.log(myDiv.attr2);//attr2

removeAttribute()彻底删除某个属性。

Element.attributes
该属性返回该元素所有属性节点的一个实时集合。该集合是一个 NamedNodeMap 对象。

//<div id="myDiv" class="div" title="haha" data-attr="special" ></div>
var myDiv = document.getElementById("myDiv");
console.log("length:"+myDiv.attributes.length);
console.log("id-nodeValue:"+myDiv.attributes.getNamedItem("id").nodeValue);
console.log("attributes[3]:"+myDiv.attributes[3].nodeName);

/** > length:4 > id-nodeValue:myDiv > attributes[3]:data-attr */

创建元素
document.createElement()

var  newNode = document.createElement("div");
newNode.appendChild(document.createTextNode("哈哈"));
document.body.appendChild(newNode);

Text类型

文本节点由Text类型表示。nodeValue的值为文本节点包含的文本。
可以通过nodeValue属性或data属性访问Text节点中包含的文本。
操作文本节点的方法。

appendData(text)//将text加到文本结尾
deleteData(offset, count)//从offset位置开始删除count个字符
insertData(offset, text)//在offset位置插入text
replaceData(offset, count, text)//替换offset到offset+count为止处的文本。
splitText(offset)//从offset位置将当前文本节点分为两个文本节点
substringData(offset, count)//子字符串

元素可以包含文本节点

//没有内容所以没有包含文本节点
<div></div>

//包含一个文本节点,空白也算内容
<div> </div>

document.createTextNode()创建文本节点

normalize()
将多个连续文本节点合并成一个节点

var el  = document.createElement("div");

el.appendChild(document.createTextNode("哈哈"));
el.appendChild(document.createTextNode(" 嘻嘻"));
console.log(el.childNodes.length);//2
el.normalize();
console.log(el.childNodes.length);//1

splitText()
指定偏移量,切割一个文本节点为两个文本节点,返回第二个文本节点。
如果指定的偏移量刚好等于原文本节点所包含字符串的长度,则返回一个内容为空的文本节点.

//指定偏移量2
var newNode = el.childNodes[0].splitText(2);
console.log(el.childNodes.length);//2

//切割后的第一个节点
console.log(el.childNodes[0].nodeValue);//哈哈
//切割后的第一个节点,从位置2开始
console.log(el.childNodes[1].nodeValue);// 嘻嘻

Comment类型

注释在DOM中通过Comment类型表示。

DocumentFragment

DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。
请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。


// <ul id="ul1"></ul>为ul添加多个列表项,为了防止页面反复渲染,创建一个DocumentFragment保存新建的列表项
var ul = document.querySelector("#ul1");
// 创建一个DocumentFragment
var fragment = document.createDocumentFragment();

var li = null;
for(var i = 0; i < 3; i++) {
  li = document.createElement("li");
  li.appendChild(document.createTextNode(i));
  fragment.appendChild(li);
}

ul.appendChild(fragment);

DOM操作

动态脚本,动样式

使用<script>元素可以向页面插入JavaScript代码,动态脚本指的是在页面加载时不存在,但将来的某一时刻通过修改该DOM动态添加的脚本。创建动态脚本也有两种方式:插入外部文件和直接插入JavaScript代码。

对于内嵌的JS代码只能同步执行,会阻塞页面。
动态加载外部文件是异步的不会阻塞页面。当script文件下载完成后,JavaScript文件通常被立即执行。
比如下面的<script>元素。

<script type="text/javascript" src="client.js"></script>

而创建这个节点的DOM代码如下所示:

function loadScript(url) {
  var script = document.createElement("script");
  script.type="text/script";
  script.src=url;
  document.body.appendChild(script);
}

loadScript("xxx.js");

操作表格

// 创建table
var table = document.createElement("table");
table.border = 1;

// 创建tbody
var tbody = document.createElement("tbody");
table.appendChild(tbody);

// tbody插入一行
tbody.insertRow(0);

// tbody第一行插入单元格
tbody.rows[0].insertCell(0);
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[0].appendChild(document.createTextNode("1"));
tbody.rows[0].cells[1].appendChild(document.createTextNode("2"));

document.body.appendChild(table);