supce's blog

DOM(一)


DOM是document object model的缩写,即:文档对象模型。DOM将HTML和XML描述为一个具有层次结构的节点树。DOM作为一个接口,可以对HTML或者XML进行添加,删除和修改。
根据 DOM,HTML 文档中的每个成分都是一个节点。DOM 是这样规定的:

  • 整个文档是一个文档节点
  • 每个 HTML 标签是一个元素节点
  • 包含在 HTML 元素中的文本是文本节点
  • 每一个 HTML 属性是一个属性节点
  • 注释属于注释节点

节点类型

JavaScript中总共有12种节点类型,这些节点类型全部继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法。
节点类型有在Node类型中定义的12个数值常量来表示:

Node.ELEMENT_NODE (1)
Node.ATTRIBUTE_NODE (2)
Node.TEXT_NODE (3)
Node.CDATA_SECTION_NODE (4)
Node.ENTITY_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)
Node.NOTATION_NODE (12)

常用属性

nodeType属性

每个节点都有一个nodeType属性,可以确定节点的类型。

<div id="test">div</div>
var test = document.getElementById("test");
if(test.nodeType == 1){
    console.log("This node is an element");
}

nodeName属性

nodeName属性保存的是元素的标签名

<div id="test">div</div>
var test = document.getElementById("test");
if(test.nodeType == 1){
    console.log(test.nodeName);  //DIV
}

节点关系

节点的关系类似于家谱。
每个节点都有一个childNodes属性来表示子节点,其中保存着一个NodeList对象。NodeList 是一种类数组对象,用于保存一组有序的节点,可以通过位置或者item()方法来访问这些节点。

var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(2);
var length = someNode.childNodes.length;

请注意,虽然可以通过方括号语法来访问 NodeList 的值,而且这个对象也有 length 属性,但它并不是 Array 的实例。可以通过一个函数把它转换为数组

function convert2Array(nodes){
    try{
        return Array.prototype.slice.call(nodes,0);
    }catch(ex){
        var array = [];
        for(var i=0;i<nodes.length;i++){
            array[i] = nodes[i];
        }
        return array;
    }
}
console.log(convert2Array({length:2,0:'first',1:'second'})); //["first", "second"]

还有一些其他关系

  • parentNode 父节点
  • previousSibling 该节点的前一个兄弟节点
  • nextSibling 该节点的后一个兄弟节点

节点操作

appendChild()

该方法用于向childNodes列表末尾添加一个节点。返回值为新增的节点。比如:

<ul id="list">
    <li>item1</li>
    <li>item2</li>
</ul>
var list = document.getElementById("list");
var item = document.createElement("li");
item.appendChild(document.createTextNode("itme3"));
list.appendChild(item);   //向ul中添加了一个li

如果传入到 appendChild() 中的节点已经是文档的一部分了,那结果就是将该节点从原来的位置转移到新位置。比如将item1与item2互换:

<ul id="list">
    <li>item1</li>
    <li>item2</li>
</ul>
var list = document.getElementById("list");
var item = list.childNodes[1];  //0为空白文本节点
list.appendChild(item);

有时候,空白文本几点会影响操作,可以写一个函数把空白文本节点删除。

function cleanWhitespace(element){   
    for(var i=0; i<element.childNodes.length; i++)   
    {   
        var node = element.childNodes[i];   
        if(node.nodeType == 3 && !/\S/.test(node.nodeValue))   
        {   
            node.parentNode.removeChild(node);   
        }   
    }   
} 

insertBefore()

这个方法接受两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个同胞节点(previousSibling),同时被方法返回。如果参照节点是null则insertBefore()appendChild()执行相同的操作。比如在item1前面插入item0:

<ul id="list">
    <li>item1</li>
    <li>item2</li>
</ul>
var list = document.getElementById("list");
var item = document.createElement("li");
item.appendChild(document.createTextNode("item0"));
list.insertBefore(item,list.firstChild);

replaceChild()

replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位置。

removeChild()

removeChild()方法为移除某个节点,参数为要移除的节点

cloneNode()

该方法用于复制调用该方法的节点,接收两个参数。

  • 当参数为true时,复制该节点和该节点下的整个子节点树
  • 当参数为false时,只复制当前节点本身

详细介绍可以参考上篇文章

normalize()

这个方法唯一的作用就是处理文档树中的文本节点。由于解析器的实现或DOM操作等原因,可能会出现文本节点不包含文本,或者接连出现两个文本节点的情况。当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述两种情况。如果找到了空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并为一个文本节点。


Document类型

Document类型表示文档。而我们常用的document对象是HTMLDocument(继承自Document类型)的一个实例。document对象也是window对象的一个属性,因此可以将其作为全局的对象来访问。

document对象的常用属性

  • documentElement属性用来获取页面中的<html>元素
  • body属性可以直接获取页面中的body元素

    var body = document.body
    
  • doctype属性可以获取对<!DOCTYPE>的引用

    console.log(document.doctype); //<!DOCTYPE html>
    
  • title属性可以获取网页的<title>元素

    //获取title
    var title = document.title;
    //设置title
    document.title = "new title";
    
  • URL属性可以获取当前页面完整的url

  • domain属性可以获取当前页面的域名

比如google首页:

document对象的常用方法

在开发过程中,经常需要取得摸个元素的引用,再执行某些操作。Document类型为此提供了两个方法。

  • getElementById()参数为元素的ID名
    IE8 及较低版本不区分 ID 的大小写。如果页面中多个元素的 ID 值相同, getElementById() 只返回文档中第一次出现的元素
  • getElementsByTagName()参数为元素的标签名,在HTML文档中该方法返回的是包含零个或多个元素的HTMLCollection对象。

    <img src="#1" name="user_name">
    <img src="#2" name="user_age">
    <img src="#3" name="user_sex">
    //获取页面中所有的<img>元素
    var images = document.getElementsByTagName("img");
    //获取长度
    console.log(images.length);
    //通过索引获取src
    console.log(images[0].src);
    //通过name获取src
    console.log(images["user_age"].src);
    
  • getElementsByName()这个方法会返回带有给定name特性的所有元素。

特殊集合

除了属性和方法,document对象还有一些特殊的集合。这些集合都是 HTMLCollection 对象,为访问文档常用的部分提供了快捷方式。

集合 描述
document.anchors 包含文档中所有带 name 特性的 <a> 元素;
document.forms 包含文档中所有的 <form> 元素
document.images 包含文档中所有的 <img> 元素
document.links 包含文档中所有带href特性的 <a> 元素

文档写入

write()writeln()
这两个方法都会接受一个字符串参数。在页面被加载的过程中,可以使用这两个方法向页面中动态地加入内容。writeln()方法会在写入末尾添加一个换行符。

current date and time is :
<script type="text/javascript">
    document.write("<p>" + (new Date()).toString() +"</p>")
</script>

Element类型

Element类型提供了对元素标签名、子节点及特性的访问。常见的属性为id,title,classname

元素的创建

可以通过document.createElement()方法创建新元素。比如创建一个div元素

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

元素特性的相关操作

取得特性

getAttribute()方法来取得属性值。

<div id="my_test_div" class="div_class" title="div_title" align="left"></div>
var div = document.getElementById("my_test_div");
console.log(div.getAttribute("id"));  //或者div.id
console.log(div.getAttribute("class"));//或者div.class
console.log(div.getAttribute("title"));//或者div.title
console.log(div.getAttribute("align"));//或者div.align

通过getAttribute()方法也可以取得自定义特性的值,以下面的元素为例:

<div id="my_test_div" my_special_attribute="hello!"></div>

这个元素包含一个名为 my_special_attribute 的自定义特性,它的值是 “hello!” 。可以像取得其他特性一样取得这个值:

var value = div.getAttribute("my_special_attribute");

设置特性

setAttribute()用于设置特性,这个方法接受两个参数:要设置的特性名和
值。如果特性已经存在,setAttribute()会以指定的值替换现有的值;如果特性不存在,setAttribute()则创建该属性并设置相应的值。

<div id="my_test_div" ></div>
var div = document.getElementById("my_test_div");
div.setAttribute("class","div_class");//或者div.class="div_class"
div.setAttribute("title","div_title");//或者div.title="div_title"
div.setAttribute("align","right");//或者div.align="right"

删除特性

removeAttribute(),这个方法用于彻底删除元素的特性。调用这个方法不仅会清除特性的值,而且也会从元素中完全删除特性。

元素子节点

元素子节点部分见节点关系部分。


Text类型

正如之前用到过的例子中,有时候需要向文档树中添加文本节点。
文本节点由Text类型表示,包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的HTML字符,但不能包含HTML代码。
在默认情况下,每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。

<div></div>//没有内容,没有文本节点
<div> </div>//有空格,有一个文本节点
<div>hello world!</div>//有内容,有一个文本节点

可以获取文本节点的引用并利用nodeValue修改它。

var textNode = div.firstChild;
textNode.nodeValue = "this is a test";

如果没有文本节点,可以创建一个文本节点,并插入到元素中

var element = document.createElement("div");
element.className = "test";
var text = document.createTextNode("hello world");
element.appendChild(text);
document.body.appendChild(element);

DOM文档中存在相邻的同胞文本节点很容易导致混乱,因为分不清哪个文本节点表示哪个字符串。另外,DOM文档中出现相邻文本节点的情况也不在少数,于是就催生了一个能够将相邻文本节点合并的方法normalize()
这个方法是由 Node类型定义的。如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue 等于将合并前每个文本节点的nodeValue值拼接起来的值。比如:

var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
var anotherTextNode = document.createTextNode("Nico!");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
console.log(element.childNodes.length); //2
element.normalize();
console.log(element.childNodes.length); //1
console.log(element.firstChild.nodeValue); // "Hello world!Nico!"

Text 类型提供了一个作用与normalize()相反的方法:splitText()
这个方法会将一个文本节点分成两个文本节点,即按照指定的位置分割nodeValue值。原来的文本节点将包含从开始到指定位置之前的内容,新文本节点将包含剩下的文本。这个方法会返回一个新文本节点,该节点与原节点的parentNode相同。

var element = document.createElement("div");
element.className = "test";
var text = document.createTextNode("Nico&Sora");
element.appendChild(text);
document.body.appendChild(element);
var newNode = element.childNodes[0].splitText(4);
console.log(element.childNodes[0].nodeValue);  //Nico
console.log(newNode.nodeValue);  //&Sora
console.log(element.childNodes.length); //2