DOM是针对HTML和XML文档的一个API。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。

节点层次

<!DOCTYPE html>
<html>
    <head>
        <title>sample page</title>
    </head>
    <body>
        <p>Hello World!</p>
    </body>
</html>

文档节点(Document node)是每个文档的根节点(在浏览器中对应的是document),在这个例子中,文档节点有两个子节点,一个是<!DOCTYPE html>(DocumentTyep类型),另一个是<html>(Element类型)。文档元素是文档最外层的元素,文档中的其他所有元素都是包含在文档元素中。每个文档只能有一个文档元素,在HTML页面中,文档元素始终是<html>元素。

Node类型

JavaScript中,所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法

每个节点都有一个nodeType属性,用于表明节点的类型.

  • 确定节点类型

if (someNode.nodeType == Node.Element_NODE){
    alert("Node is an element");  // 表明该节点是一个HTML元素
}
或者
if (someNode.nodeType == 1){
    alert("Node is an element");  // 表明该节点是一个HTML元素
}

nodeName和nodeValue

  • 如果node是HTML标签,则nodeName的值就是元素的标签名,对于非文档节点,返回null

if (someNode.nodeType == 1){
    tagName = someNode.nodeName;
}
  • 对于文档节点(HTML标签)来说, nodeValue返回null. 对于text, comment, 和 CDATA 节点来说, nodeValue返回该节点的文本内容. 对于 attribute 节点来说, 返回该属性的属性值.

节点之间的关系(node的一些属性):

  • node.childNodes

  • node.parentNode

  • node.previousSibling

  • node.nextSibling

  • node.firstChild

  • node.lastChild

操作节点(node的一些方法)

  • node.appendChild():返回新增的节点,这个返回的节点是存在于DOM树中的节点,可以通过上面的属性访问DOM树中的其他节点

var returnNode = parentNode.appendChild(newNode);
  • insertBefore(newNode, baseNode): 同样会返回一个新节点,同上

  • replaceChild(newNode, replacedNode): 同样会返回一个新节点,同上

  • removeChild(node):会返回一个节点,该节点不存在于DOM树中

  • cloneNode:

// 执行深复制,即复制节点及其整个子树
var returnNode = someNode.cloneNode(true);

// 执行浅复制,只复制节点本身
var returnNode = someNode.cloneNode(false);

Document类型

JavaScript通过Document类型表示文档。在浏览器中,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。document对象是window对象的一个属性,因此可以作为全局对象来访问

文档子节点

Document节点(浏览器中是document)的子节点可能是DocumentType(最多一个,例如<!DOCTYPE html>)、Element(最多一个例如 <html>)、ProcessingInstruction、Comment

  • document.documentElement 始终指向HTML页面中的文档元素(<html>)

  • document.body直接返回对<body>元素的引用

  • document.doctype会返回对<! DOCTYPE HTML>的引用

  • document.title返回文档标题的内容(只读)

  • document.childNodes会返回[<! DOCTYPE HTML>,html] (如果存在DOCTYPE的话)

文档信息:(可以获取或者设置这些属性)

  • document.title

  • document.URL: 包含页面完整的URL(地址栏中的URL)

  • document.domain:

  • document.referrer: 链接到当前页面的那个页面的URL

查找元素

  • document.getElementById()

  • document.getElementsByTagName():返回HTMLCollection对象

  • document.getElementsByName():返回带有给定name特性的所有元素,返回HTMLCollection对象

文档写入:

  • document.write():原样写入传入的字符串

  • document.writeln(): 在字符串的末尾添加一个换行符

...
<body>
    <p>the current date and time is:
    <script>
        document.write("<strong>" + (new Date().toString() + "</strong>");
    </script>
</body.
  • 页面呈现过程 中,通过上面的方式可以在指定位置输出内容

  • 这两个方法如果在页面加载结束后被调用,那么输出内容会重写整个页面

Element类型

要访问元素的标签名,可以使用nodeName属性或者tagName属性,这两个属性会返回相同的值。注意在HTML中tagName会返回大写的标签名,在XML中返回值和代码中的标签一致。

<div id="myDiv"></div>

var div = document.getElementById("myDiv");
alert(div.tagName); // DIV
alert(div.tagName == div.nodeName); //true 
alert(div.tagName.toLowerCase() == "div"); // true

HTML元素

可以直接访问和修改的属性:

  • id: element.id

  • title: element.title

  • className: element.className

  • lang

  • dir: 文字方向

<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr"></div>

var div = document.getElementById("myDiv");
alert(div.id);
alert(div.className);
alert(div.title);
alert(div.lang);
alert(div.dir);

取得特性: getAttribute

<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr"></div>

var div = document.getElementById("myDiv");
alert(div.getAttribute("id"));
alert(div.getAttribute("class");
alert(div.getAttribute("title");
alert(div.getAttribute("lang");
alert(div.getAttribute("dir");

对于style、onclick这种特殊的特性,getAttribute()只能返回相应代码的字符串,因此在通过JavaScript以编程的方式操作DOM时,开发人员经常不使用getAttribute(),而是只使用对象的属性(div.id)

任何元素的所有特性,也都可以通过DOM元素本身的属性来访问。不过只有公认的特性才会以属性的形式添加到DOM对象中。 以下面的元素为例:

<div id="myDiv" align="left" my_special_attribute="hello!"></div>

由于id和align在html中是<div>的公认特性,因此在该元素的DOM对象中也将存在对应的属性。

alert(div.id); // "myDiv"
alert(div.my_special_attribute); // undefined
alert(div.align); //"left"

设置特性--setAttribute()

setAttribute()接受两个参数,要设置的特性名和值。如果特性已经存在,则会替换已有的值,如果特性不存在,则会创建该属性并设置相应的值。

<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr"></div>

var div=document.getElementById("myDiv");
div.setAttribute("id","someOtherId");

移除属性--removeAttribute()

<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr"></div>

var div=document.getElementById("myDiv");
div.removeAttribute("id");

attributes属性:使用不方便不经常使用,但是可以用来遍历元素特性

Element类型是使用attribute属性的唯一一个DOM节点类型。attribute属性中包含一个NamedNodeMap。元素的每个特性都有一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。NamedNodeMap对象拥有下列方法。

  • getNamedItem(name):返回nodeName属性等于name的节点

  • removeNamedItem(name): 移除nodeName属性等于name的节点

  • setNamedItem(node): 向列表中添加节点,以节点的nodeName属性为索引

  • item(pos): 返回位于数字pos位置处的节点

var id = element.attributes.getNamedItem("id").nodeValue;

var id = element.attributes["id"].nodeValue;
  • 使用不方便不经常使用,但是可以用来遍历元素特性

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(' ');
}

创建元素

document.createElement()方法可以创建新元素,这个方法只接受一个参数,即要创建元素的标签名

var div = document.createElement("div");
div.id = "myDiv";
div.className = "box";
// 此时新元素还没有添加到文档树中,因此设置这些特性不会影响浏览器的显示
// 把新元素添加到文档树中:appendChild()、insertBefore()、replaceChild()
document.body.appendChild(div);

元素的子节点

如果想通过某个特定的标签名取得子节点或者后代节点,可以在元素上调用getElementsByTagName()方法

Text类型

文本节点由Text类型表示,包含的是可以按照字面解释的纯文本内容,但是不能包含HTML代码

重要的特点:

  • nodeType的值为3

  • nodeName为#text

  • nodeValue的值为节点包含的文本

  • parentNode是一个Element

  • 没有(不支持)子节点

有一些操作节点中文本的方法:

  • appendData(text): 将text添加到末尾

  • deleteData(offset, count): 从offset指定的位置开始,删除count个字符

  • insertData(offset, text):

  • replaceData(offset, count, text)

  • splitText(offset): 从offset指定位置将当前文本节点分成两个 文本节点

  • ...

如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue等于将合并前每个文本节点的nodeValue值拼接起来的值。

DocumentFragment类型:

在所有的节点类型中,只有DocumentFragment在文档中没有对应的标记。DOM规定文档片段是一种“轻量级”的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。

虽然不能把文档片段直接添加到文档中,但是可以将它作为一个仓库来使用,即在里面保存将来可能会添加到文档中的节点。

创建文档片段的方式:document.createDocumentFragment()

var fragment = document.createDocumentFragment();
var ul = document.getElementById('myList');
var li = null;

//位ul添加三个li
for(var i=0; i<3; i++){
    li = document.createElement("li");
    li.innetText = "Item" + (i+1);
    fragment.appendChild(li);
}

ul.appendChild(fragment);

上面的例子是为ul元素添加3个列表项,如果逐个地添加列表项,将会导致浏览器频繁渲染新信息。为避免这个问题,可以使用frament,将所有列表项先添加到fragment中,最后将fragment添加到文档中。通过这种方式减少了浏览器的渲染次数。

DOM操作技术

动态脚本

  • 插入外部文件

function loadScript(url){
    var script = document.createElement('script');
    script.type = "text/javascript";
    script.src = url;
    document.body.appendChild(script);
}
  • 行内方式

var script = document.createElement('script');
script.type = "text/javascript";
script.appendChild(document.creatTextNode('function sayHi(){alert('hi');}'));
document.body.appendChild(script);

动态样式

function loadStyles(url){
    var link = document.createElement("link");
    link.rel = "stylesheet";
    link.type = "text/css";
    link.href = url;
    var head = document.getElementsByTagName('head')[0];
    head.appendChild(link);
}

操作表格

HTML DOM为<table>, <tbody>, <tr>元素添加了一些属性和方法,通过这些属性和方法,我们可以方便的对表格进行操作

使用NodeList

NodeList,NamedNodeMap和HTMLCollection都是动态的,每当文档结构发生变化时,它们都会得到更新。

一般来说,应该尽量减少访问NodeList的次数,因为每次访问NodeList,都会运行一次基于文档的查询,所以可以考虑将从NodeList中取出的值缓存起来。

总结

DOM操作往往是JavaScript程序中开销最大的部分,而因访问NodeList导致的问题为最多。NodeList对象都是“动态的”,这就意味着每次访问NodeList对象,都会运行一次查询。有鉴于此,最好的办法就是尽量减少DOM操作。


jhhfft
590 声望40 粉丝

Write the Code. Change the World.