目录
1、节点类型 2、节点遍历 3、元素节点遍历 4、Document类型
1、获取节点 2、创建节点 3、修改节点内容 4、插入节点 5、删除节点
1、property accessor属性访问器 2、getAttribute/setAttribute 3、dataset
文档对象模型(Document Object Model,DOM),用节点对象的方式描述对应的html,是一系列可以操作节点的API规范。DOM在浏览器中通过js实现,可以通过js调API来操作页面结构、样式等。
DOM包括:
DOM Core:DOM核心结构、API的定义;
DOM HTML:定义html如何转化成对象,操作节点、结构;
DOM Style:把样式转化为对象,操作样式;
DOM Event:事件模型,让页面能够响应用户操作。
一、DOM树
DOM可以将任何html文档描绘成一个由多层节点构成的树形结构,以文档节点为根结点。
1、节点类型
节点类型由数字常量表示。元素节点ELEMENT_NODE(1)/文本节点TEXT_NODE(3)/DOCUMENT_NODE /COMMENT_NODE/DOCUMENT_TYPE_NODE。
if(someNode.nodeType==1){ //判断节点类型,如果是元素节点
value=someNode.nodeName; //获取元素节点的标签名称
}
2、节点遍历
node.parentNode/firstChild/lastChild/previousSibling/nextSibling,关系指针都是只读的。
node.childNodes获取的是类数组对象,用于保存直接子节点,基于DOM结构动态进行查询的结果。访问某个节点node.childNodes[0]或node.childNodes.item(1)。
node.ownerDocument,指向表示整个文档的文档节点。
function convertToArray(nodes){ //把类数组对象转化为数组,如childNodes、argument
var array=null;
try{
array=Array.prototype.slice.call(nodes,0); //IE8及以下不支持
}catch(ex){ //手动枚举所有成员
array=new Array();
for(var i=0,len=nodes.length;i<len;i++){
array.push(nodes[i]);
}
}
return array;
}
3、元素节点遍历
element.firstElementChild/lastElementChild/nextElementSibling/previousElementSibling
在IE低版本不支持元素节点,用“节点遍历+节点类型判断”作兼容。
4、Document类型
document对象表示整个html页面,是window对象的属性,可以通过全局的方式直接使用document访问该节点。只有唯一的子元素html。
var html=document.documentElement;
alert(html===document.childNodes[0]); //true
alert(html===document.firstChild); //true
属性:
document.documentElement,指向<html>元素;
document.body,指向<body>元素;
document.title,获取或修改当前文档的标题;
document.referrer,保存着链接到当前页面的那个页面的URL,没有来源则为空字符串,如用于安全判断,如果把不希望直接访问的页面嵌入到其它域名的页面里,则页面被盗链,可进行细微的处理;判断当前页面上游是否在产品内部,如果是别的未知页面,可以把返回按钮替换为首页按钮。
document.URL,返回页面完整的URL。
document.domain,只包含页面的域名。将两个页面的domain设置为相同的值,就可以互相访问对方包含的js对象。
二、节点操作
页面渲染后如何修改页面内容和结构。
<div id="users"> //对这一段html代码进行操作
<h2>6662人在学习该课程:</h2>
<ul>
<li class="user">Satoshi</li>
<li class="user">春来草青</li>
<li id="lastUser" class="user last">Kash</li>
</ul>
</div>
1、获取节点
通过已获取到的节点的节点关系(父子关系、兄弟关系)来获取其它节点,可维护性差,因节点关系紧密,容易获取不到。可以通过接口获取节点。
(1)getElementById
var element=document.getElementById(id);
从文档中通过指定id获取元素,获取到的是包含很多属性和接口的节点对象。因为id在整个文档中,所以通过document调用。
IE8-不区分id大小写,IE7-表单元素的name属性值如果与指定id匹配会被返回,且该方法只返回第一次出现的元素。保证id及表单元素的name在不区分大小写时唯一。
(2)getElementsByTagName
var collection=element.getElementsByTagName(tagName);
通过标签名获取元素,获取element的后代元素中具有指定的标签名称的集合。可以通过下标[]获取该集合中的元素,[]中可以是数值或元素的name值字符串。传入”*”,获取所有后代元素。返回的collection集合是动态的,后续对节点的操作会使该返回值动态变化。
var users=document.getElementById('users');
var a=users.getElementsByTagName('li'); //a.length==3
var lastUser=document.getElementById('lastUser');
lastUser.parentNode.removeChild(lastUser); //a.length==2,a是动态的
(3)getElementsByClassName
var collection=element.getElementsByClassName(className);
通过类名获取元素,空格分隔指定多个类名,获取同时具有这几个类名的元素,类名顺序无关。通过下标获取该集合中的元素。返回的collection集合是动态的。IE8-不支持该方法,作兼容。
users.getElementsByClassName("user")[2]; //li.user.last
users.getElementsByClassName("user last"); //li.user.last
function getElementsByClassName(root,classNames){
if(root.getElementsByClassName){//特性侦测
return root.getElementsByClassName(classNames);//优先使用W3C规范
}else{
var elements=root.getElementsByTagName('*');//获取所有与后代元素
var result=[];
var element,
classNameStr,
flag;
classNames=classNames.split(' ');//拆分类名至数组
for(var i=0;element=elements[i];i++){
//选择包含类名的元素
classNameStr=' '+element.className+' ';
flag=true;
for(var j=0,name;name=classNames[j];j++){
if(classNameStr.indexOf(' '+name+' ')==-1){
flag=false;
break;
}
}
if(flag){
result.push(element);
}
}
return result;
}
}
(4)querySelector/All
var list=element.querySelector/All(selector);
获取指定元素的后代元素中符合选择器的节点,第一个符合条件的元素或所有元素的列表。返回的列表是静态的,一旦获取就不会变化。IE6-7不支持,IE8部分支持。
var users=document.querySelector("#users");
users.querySelectorAll(".user");
document.querySelectorAll("#users .user");
2、创建节点
(1)createElement
var element=document.createElement(tagName);
创建指定标签名的元素。
(2)cloneNode
var deepList=node.cloneNode(true); //深克隆,包括复制节点及其后代节点
var shallowList=node.cloneNode(false); //浅克隆,复制节点本身
克隆,创建调用这个方法的节点的一个完全相同的副本。只复制属性和子节点,不复制添加到DOM节点中的js属性。
3、修改节点内容
(1)textContent
element.textContent
节点及其后代节点的文本内容。IE9-不支持。
user_last.textContent; //"Kash",返回该节点内容
user_last.textContent="Tom"; //修改文本内容
(2)innerText
element.innerText
节点及其后代节点的文本内容,与textContent基本一样。不是W3C规范,但是经常用到,FireFox不支持。作兼容。
if(!('innerText' in document.body)){//特性检测
HTMLElement.prototype._defineGetter_("innerText",function(){
return this.textContent;
});
HTMLElement.prototype._defineSetter_("innerText",function(s){
return this.textContent=s;
});
}
(3)innerHTML
element.innerHTML
给节点添加HTML内容。
users.innerHTML; //返回节点users下面所有的HTML文字
users.innerHTML=""; //删除一批节点
在原来基础上直接增加ul.innerHTML+=’’,相当于重新设置了ul,原来设置的节点事件、样式等被清除。所以需要先创建空的li节点。
var li=document.createElement('li');
li.className='user';
ul.appendChild(li);
//创建节点、插入节点、设置属性
li.innerHTML='<img src="4.jpg">\<a href="/user/4">lifeng</a>';
在低版本的IE有内存泄漏,如上述事件状态被清掉,内存得不到回收。如果填写的是一段代码,直接获取用户信息等,会有安全问题。建议仅用于新结点,且内容是可控的,不是用户填的内容,如果是用户填的内容也要确保其中没有标签。
(4)DocumentFragment
在文档中没有对应的标记,保存将来要添加到文档中的节点,通过appendChild()或insertBefore()批量将文档片段中内容添加到文档中。避免浏览器反复渲染新信息。
//效率不高,每append一次,浏览器都会重复一次ul标签的元素列表
for(var x=0;x<10;x++){
var li=document.createElement("li");
li.innerHTML="List item "+x;
listNode.appendChild(li);
}
//效率提高,把li建立好,批量添加,但仅用于向空元素中添加内容
var html="";
for(var x=0;x<10;x++){
html+="<li>List item "+x+"</li>";
}
listNode.innerHTML=html;
//效率提高,且不影响已有元素的样式
var frag=document.createDocumentFragment();
for(var x=0;x<10;x++){
var li=document.createElement("li");
li.innerHTML="List item "+x;
frag.appendChild(li);
}
listNode.appendChild(frag);
4、插入节点
(1)appendChild
var achild=element.appendChild(achild);
在元素最后子节点后面插入,并返回新增的节点,element.lastChild==achild。如果添加的节点是已有节点,任何DOM节点不能同时出现在文档不同位置,所以相当于把该节点转移到最后而不是克隆一份。
(2)insertBefore
var achild=element.insertBefore(achild,referenceChild);
把节点插入元素的指定节点之前,并返回新增节点,指定节点是null则插在最后。
var users=document.getElementById('users');
var ul=users.getElementsByTagName('ul')[0]; //[]获得对象的一个元素
var li=document.createElement('li'); //创建节点
li.className='user'; //设置属性
li.innerText='lifeng'; //设置文本内容
//ul.appendChild(li);插入节点
ul.insertBefore(li,ul.firstChild); //插入节点
5、删除节点
(1)removeChild
var child=element.removeChild(child);
移除节点并返回。
var user2=users.getElementsByClassName('user')[1];
user2.parentNode.removeChild(user2);
(2)replaceChild
var achild=element.replaceChild(achild,referenceChild);
返回要替换的节点。
上面四个方法都是操作父节点的子节点,在没有子节点的节点上调用这些方法,会导致错误。
三、属性操作
每个html属性对应相应的DOM对象属性,通过js来获取html属性进行属性操作。
<div>
<label for="userName">用户名</label>
<input id="userName" type="text" class="u-txt">
</div>
1、property accessor属性访问器
element.propertyName;
非自定义的属性以属性的形式添加到DOM对象中。通过属性访问符访问的属性是转换过的实用对象,把字符串转换为相应类型,可以直接使用。通用性差,有名字异常,如果属性名与关键字等冲突,需要采用另外的名字;扩展性差,每增加一个html属性,就需要增加DOM属性。
label.htmlFor; //"userName"
input.className; //"u-txt",input是DOM对象
input["id"];//"userName"
input.value='wwq@163.com'; //写操作,在input上增加属性
input.disabled=true; //只有true、false两个取值的属性,无论设置为什么值,只要出现了就是true
2、getAttribute/setAttribute
var attribute=element.getAttribute(attributeName);
element.setAttribute(name,value); //替换值或创建新属性
属性名不区分大小写操作属性字符串。通用性好,没有名字冲突问题;但获取到的都是String类型的字符串,不能直接使用。
input.getAttribute("class");//"u-txt"
input.setAttribute("value","wwq@163.com");
input.setAttribute("disabled","");
3、dataset
element.dataset.propertyName
获取html元素上的自定义属性,这些属性的值都是String型。加data-前缀,用于在元素上保存数据,对应的DOM对象上的属性名将data-和-去掉。
保存跟节点相关的数据用来以后使用。如通过事件显示卡片,数据通过Ajax从后台获取,获取到数据时把数据存在该节点上,当鼠标hover上,把数据填到卡片上。
在低版本IE没有实现,可以用getAttribute()和setAttribute()获取和设置自定义属性兼容。
<div id="user" data-id="123456" data-account-name="hsg"
data-name="Scott" data-email="476699052@qq.com">hsg</div>
四、样式操作
与用户交互后引起一个或多个元素样式发生变化,通过js来动态修改样式。
整张页面所有的外部、内部样式表对应的DOM对象document.styleSheets;
外部样式表对应的DOM对象element.sheet,link元素对应对象的sheet属性,对应.css外部样式表;
内部样式表对应的DOM对象:element.sheet,style元素对应对象的sheet属性;
内嵌样式对应的DOM对象:element.style。
element.sheet //样式表对应的DOM对象
element.sheet.cssRules[1] //对应第1条CSS属性的声明
element.sheet.cssRules[1].style //style是属性名和属性值的键值对
element.sheet.cssRules[1].style.lineHeight //获取对应的属性值
element.sheet.cssRules[1].selectorText //这条rule的选择器
element.style.color //获取对应的属性值
style对象是CSSStyleDeclaration类的一个对象,是CSS属性名和属性值的键值对。
1、更新样式
(1)增加内嵌样式
在某元素的style属性增加样式,内嵌样式表优先级最高。更新一个属性需要一条语句,且不是我们熟悉的CSS。
element.style.borderColor='red';
element.style.color='red';
一条语句可以更新一个元素所有的CSS属性,具有很强的扩展性,且是熟悉的CSS写法。
element.style.cssText='border-color:red;color:red;';
样式混在逻辑中,在js中写的样式,如果要修改样式,只能在js中修改。
(2)更新class
在元素的class上增加类名。
.invalid{
border-color:red;
color:red;
}
element.className+='invalid';
(3)更换样式表
一次更新很多元素样式,如换肤。把style.css拆分成两个CSS。变换皮肤的两个CSS文件中,所有样式选择器相同,后面的属性值不同。
也可以添加或删除样式表来增加或删除样式。
<link rel="stylesheet" href="base.css"> //基本样式
<link id="skin" rel="stylesheet" href="skin.spring.css"> //换肤变化的样式
Util.addEventListener($('change'),'click',changeSkin);
function changeSkin(){
$('skin').href="skin.summer.css";
}
2、获取样式
(1)element.style
只能获取元素style属性中的样式,获取到的不一定是实际样式值。
(2)window.getComputedStyle()
var style=window.getComputedStyle(element[,pseudoElt]);
获取实际样式,传入当前想获取样式的元素。获取到style对象,只读。IE9-使用element.currentStyle()作兼容。
window.getComputedStyle(element).color;