DOM

DOM概览

DOM(文档对象模型)是表示和操作HTML文档内容的基础API。用DOM树对象表示HTML文档的嵌套元素,包括HTML标签、表示文本字符串的节点,也可能包含HTML的注释节点。

选取文档元素

树形的根部是document节点,代表整个文档,代表HTML元素的是Element节点,代表文本的是text节点。这些节点都是Node的子类。JavaScript避免不了要操作DOM,这时可以使用全局变量document来引用DOM对象,同时,DOM也定义了许多方式来选取元素,如通过指定的id属性,name属性,标签名字,指定的CSS类等。

1、通过id查找多个元素:

/*函数接受任意多个字符串参数,每个参数将作为元素id传入*/
function(/ids.../){
    var elements = {};
    for(var i=0;i<arguments.length;i++){
        var id = arguments[i];
        var elt = document.getElementById(id);
        if(elt == null){
            thow new Error("No element with id:"+id);
        }else{
            elements[id] = elt;
        }
    }
    return elements;
}
//低于IE8版本的浏览器中,getElementById()方法对匹配元素的ID不区分大小写,同时也会匹配到那么属性符合条件的元素。所以,注意设置name和id属性的时候尽量让这两个元素不要重名。

2、用过name属性和标签名来查找元素,分别使用getElementByName()和getElementsByTagName()方法,这两个方法都是返回一个NodeList对象。像document.images或者document.forms的属性为HTMLCollection对象
这两种都是只读的类数组对象,他们有length属性,也可以像数组一样索引(只能读),但是不能在这两类对象上直接调用数组的方法,但是可以间接的调用:

//获取p标签中的内容
var content = Array.prototype.map.call(document.getElementByTagName("p"),function(e){ return e.innerHTML;});

值得注意的是NodeList对象HTMLCollection对象都是实时的。
同时,Element类也定义了getElementsByTagName()方法,该方法只在调用该方法的元素对象的后代中选取元素。

3、通过HTML的class属性选择元素,使用getElementsByClassName()方法,返回的是一个实时的NodeList对象,该方法需要一个字符串参数,可以是由多个空格隔开的标识符组成。

//查找包含fatal和error类的元素,使用
var ele = getElementsByClassName('fatal error');

4、使用querySelector方法返回文档中匹配指定 CSS 选择器的第一个元素。

document.querySelector(CSS selectors);
document.querySelector("p");//返回第一个p标签元素
document.querySelector(".container");//返回class等于container的第一个元素
document.querySelector(".container header");//返回class等于container中的第一个header子元素

5、使用querySelectorAll方法返回所有文档中匹配指定 CSS 选择器的元素,与querySelector相同,只不过querySelectorAll方法返回一个匹配的nodeList。

文档结构

一旦从文档中选取了一个元素,有时需要查找文档中与之在结构上相关的部分,这种结构可以看作一个节点对象树。

1、节点树的文档。我们之前说的Document对象,和他的Element对象,文档中表示文本的Text对象都是属于Node对象,对于一个节点来讲,他具有以下的属性,

parentNode:父节点(document没有父节点,值为null),任何元素的父节点都不可能是Text。任何Elemnt的parentNode总是一个Element。
childNodes:子节点的实时表示
firstChild、lastChild:子节点的第一个和最后一个。不存在则为nul
nextSibling、previousSibling:兄弟节点中的前一个和下一个。
nodeType:节点的类型(9代表Document节点,1代表Element节点,3代表Text节点,11代表DocumenFragment节点)
nodeValue:Text节点的文本内容
nodeName:元素的标签名(大写)

例如有这样的一段HTML:<html><head></head><body>hello word!</body></html>
下面的方法可以获取到body

document.childNodes[0].childNodes[1];//选择到了body元素节点的引用
document.firstChild.firstChild.nextSibling;

API对文档的变化极其敏感,若我们在headhtml之间插入一个新行,那么表示这个新行的Text节点就是文档下面的第一个子节点了。

2、元素树的文档。这个应该是比较熟悉的内容,当我们想忽略部分文档,如Text节点的时候,可以使用另一个API(基于元素文档遍历API):

第一部分,children属性

该属性类似childNodes,也是一个NodeList对象,但是它只包含Element。并且Text节点和Commont节点没有children属性。

第二部分,Element属性

//类似于firstChild和lastChild,不同的是只代表子Element
firstElementChild,lastElementChild
//类似于nextSibling和lastSibling,不同的是只代表兄弟Element
nextElementSibling,previousElementSibling
//子元素的个数,和children.length相等
childElementCount

元素的属性

元素的属性应该都是比较熟悉的了,如id,class,src,以及一些时间的处理如onclick等,值得注意的是,有些属性在JavaScript中是保留字,如class,在JavaScript中把他变为ClassName。

getAttribute()//获取html属性,返回的是一个字符串,
setAttribute()//设置元素的属性
hasAttribute()//判断元素属性是否存在,
removeAttribute()//完全删除属性

var width = parseInt(images.getAttribute("WIDTH));
images.setAttribute('class','thumbnail");

还有一种使用Element属性的方法,Node类型定义了attribute属性。

document.body.attributes[0];//返回body元素的第一个属性
document.body.attributes.bgcolor;//返回body元素的第一个属性

html5文档中中表明,任意以data-为前缀的小写的属性名字都是合法的。这些数据集属性会以属性的形式存在于该元素的dataset属性内。如data-x的值保存在dataset.x内,带连字符的属性采用驼峰命名方式。

元素的内容

Element的innerHTML属性会返回元素的内容,内容可以是HTML字符串,也可以是纯文本的字符串。
查询和插入纯文本形式的内容,可以使用Node对象的textContent属性实现,在ie中使用innerHTML替代。

/**
*一个参数,返回element的textContent或者innerText
*两个参数,设置elemnet的textContent或者innerText
*/
function(element,value){
    var content = element.textContent;
    if(value === undefined){
        //没有传递value
        if(content !== undefined) return content;
        else return element.innerText;
    }
    else{
        if(content !== undefined) element.textContent = value;
        else element.innerText = value;
    }
}

创建、插入和删除节点

创建新的元素节点可以使用Document对象的createElement()方法。给方法传递元素的标签名(不区分大小写)。创建Text节点可以使用document.createTextNode()方法。

var newNode = document.createTextNode("this is a textNode");
var s = document.createElement("script")

也可以选择clone已经存在的节点作为创建节点的另一种方式,每个节点都有一个cloneNode()方法,该方法返回该节点的一个全新副本, 给方法传递参数true可以递归的复制所有的后代节点。

var cloneNode = document.getElementById("test").cloneNode(true);

将创建的节点插入到文档中的方法有appendChild()insertBefore()
appendChild()在需要插入的元素上调用,并将节点作为他最后一个子节点插入。

var ele = document.getElementById("test");
var span = document.createElement("span");
ele.appendChild(span);

insertBefore()appendChild()类似,也是在被插入的元素节点上调用.不同的是insertBefore()接受两个参数,第一个是待插入的元素,另一个是在被插入元素中已经存在的节点(该方法将新节点插入到该节点之前)。

//当位置索引大于被插入元素的子元素个数时,元素会被插入到末尾
ele.insertBefore(span,ele.children[0]);

当用这两个方法将已经存在于文档中的节点再插入一次的时候,那个节点将自动从他当前的位置删除,并在新的位置插入,不需要显示删除节点。


removeChild()从文档中删除节点,同样也是在被删除节点的父节点上调用,被删除节点作为参数传入方法。

n.parentNode.removeChild(n)

replaceChild()方法删除一个子节点,并用一个新的子节点取而代之。在父节点上调用该方法,第一个参数是新节点,第二个参数是需要替代的节点。

n.parrentNode.replaceChild(n.parrentNode.replaceChild(document.createTextNode("this is a text node"),n);

replaceChild()另一种用法:
//用元素包裹节点n
function embolden(n){
//假如参数为字符串而不是节点,将其当作元素的id
if(typeof n == ‘string’) n = document.getElementById(n);
var parrent = n.parrentNode; //获得n的父节点
var b = document.createElement(“b”);
parrent.replaceChild(b,n);
b.apperndChild(n);
}

文档片段

DocumentFragment是一种特殊的Node,他做为一个节点的临时容器存在。使用如下方法创建一个文档片段:

var frag = document.createDocumentFragment()

文档碎片是独立的,没有父节点,但是可以有任意多的子节点,所以插入元素的方法在文档碎片上同样适用。文档片段可以将一组节点被当做一个节点来看待,当我们把一个文档片段插入到文档中的时候,实际上是将文档片段中的节点插入到了文档中,二不是文档片段本身。