DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。
浏览器会根据 DOM 模型,将结构化文档(比如 HTML 和 XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。
DOM 只是一个接口规范,可以用各种语言实现。所以严格地说,DOM 不是 JavaScript 语法的一部分,但是 DOM 操作是 JavaScript 最常见的任务,离开了 DOM,JavaScript 就无法控制网页。另一方面,JavaScript 也是最常用于 DOM 操作的语言。后面介绍的就是 JavaScript 对 DOM 标准的实现和用法。
DOM
提示:Document 对象是 Window 对象的一部分,可通过 window.document 属性对其进行访问
DOM 的最小组成单位叫做节点(node)。文档的树形结构(DOM 树),就是由各种不同类型的节点组成。每个节点可以看作是文档树的一片叶子。
节点的类型有七种。
浏览器提供一个原生的节点对象Node,上面这七种节点都继承了Node,因此具有一些共同的属性和方法。
所有 DOM 节点对象都继承了 Node 接口,拥有一些共同的属性和方法,可通过Node.prototype查看共有属性和方法。
Node常用属性:
Node属性 | 描述 |
---|---|
nodeType | 节点的类型 |
nodeName | 返回节点的名称 |
nodeValue | 返回一个字符串,表示当前节点本身的文本值,该属性可读写;只有文本节点(text)、注释节点(comment)和属性节点(attr)有文本值,因此这三类节点的nodeValue可以返回结果,其他类型的节点一律返回null |
childNodes | 返回一个类似数组的对象(NodeList集合),成员包括当前节点的所有子节点 |
parentNode | 返回当前节点的父节点。对于一个节点来说,它的父节点只可能是三种类型:元素节点(element)、文档节点(document)和文档片段节点(documentfragment) |
previousSibling | 返回当前节点前面的、距离最近的一个同级节点。如果当前节点前面没有同级节点,则返回null |
nextSibling | 返回紧跟在当前节点后面的第一个同级节点。如果当前节点后面没有同级节点,则返回null |
firstChild | 返回当前节点的第一个子节点,如果当前节点没有子节点,则返回null |
lastChild | 返回当前节点的最后一个子节点,如果当前节点没有子节点,则返回null |
textContent | 返回当前节点和它的所有后代节点的文本内容 |
Node常用方法:
Node方法 | 描述 |
---|---|
hasChildNodes() | 返回一个布尔值,表示当前节点是否有子节点 |
appendChild() | 接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点。该方法的返回值就是插入文档的子节点。 |
insertBefore() | 接受两个参数,第一个参数是所要插入的节点newNode,第二个参数是父节点parentNode内部的一个子节点referenceNode。newNode将插在referenceNode这个子节点的前面。返回值是插入的新节点newNode |
removeChild() | 接受一个子节点作为参数,用于从当前节点移除该子节点。返回值是移除的子节点 |
replaceChild() | 用于将一个新的节点,替换当前节点的某一个子节点,接收两个参数 parentNode.replaceChild(newChild, oldChild); |
cloneNode() | 用于克隆一个节点。它接受一个布尔值作为参数(默认false),表示是否同时克隆子节点。它的返回值是一个克隆出来的新节点。 |
normalize() | 用于清理当前节点内部的所有文本节点(text)。它会去除空的文本节点,并且将毗邻的文本节点合并成一个,也就是说不存在空的文本节点,以及毗邻的文本节点。 |
具体案例可参考document节点
document.nodeName;//'#document'
document.nodeType;//9
document.nodeValue;//null
document.firstChild;
document.lastChild;
// HTML 代码如下
// <div id="d1">hello world</div>
var div = document.getElementById('d1');
div.nodeName // "DIV"
div.firstChild.nodeValue // "hello world" div.firstChild属于文本节点
//克隆节点
document.querySelector('ul').cloneNode(true);//包括当前节点下面的子节点
该方法有一些使用注意点。
(1)克隆一个节点,会拷贝该节点的所有属性,但是会丧失addEventListener方法和on-属性(即node.onclick = fn),添加在这个节点上的事件回调函数。
(2)该方法返回的节点不在文档之中,即没有任何父节点,必须使用诸如Node.appendChild这样的方法添加到文档之中。
(3)克隆一个节点之后,DOM 有可能出现两个有相同id属性(即id="xxx")的网页元素,这时应该修改其中一个元素的id属性。如果原节点有name属性,可能也需要修改。
不同的nodeType值对应的节点:
Document几个重要的属性:
属性 | 描述 |
---|---|
document.documentElement | 返回 html元素 |
document.body | 返回 body 元素 |
document.title | 返回 title 元素 |
document.domain | 返回 返回文档服务器的域名(可获取也可修改,但是只能从高级向低级修改,不能从低级向高级修改) |
document.referrer | 返回引用的 URI(链接文档) |
//获取页面title
document.title;//'JavaScript HTML DOM 文档'
//修改页面title
document.title='JavaScript';
//获取页面域名
document.domain;//'csdn.net'
//修改页面域名跟location.href不同,location.href可以修改为任意域名,但document.domain只能从低级到高级修改,不能从高级向低级修改(二级三级域名改一级域名可以,一级改二级三级不可以)
location.href='http://baidu.com';
document.domain='http://baidu.com';//报错
Document 对象方法
1、获取节点
方法名 | 描述 |
---|---|
getElementById() | 返回对拥有指定 id 的第一个对象的引用。 |
getElementsByName() | 返回带有指定名称的对象集合。 |
getElementsByTagName() | 返回带有指定标签名的对象集合。 |
getElementsByTagName() | 返回带有指定标签名的对象的集合 |
document.querySelector() | 接受一个 CSS 选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null |
document.querySelectorAll() | 与querySelector用法类似,区别是返回一个NodeList对象,包含所有匹配给定选择器的节点 |
注意:后三个的写法elements是带S的;后三个得到都是一个集合,用数组的查找方式获取对应标签,下标从0开始
// HTML 代码如下
// <div id="para1">hello world</div>
document.getElementById('para1');
// 表单为 <form name="x"></form>
var forms = document.getElementsByName('x');
forms[0].tagName // "FORM"
//查找类
document.getElementsByClassName(names);//单个类
document.getElementsByClassName('foo bar');//多个类,空额分割
//查找标签名
document.getElementsByTagName('*'); //查找所有标签
document.getElementsByTagName('p')[0]; //查找第一个p标签
//HTML代码
// <div class="myclass">hello world</div>
// <div class="myclass">第二行</div>
// <div class="myclass">第三行</div>
document.querySelector('.myclass'); //得到第一个类名为myclass的标签
document.querySelectorAll('.myclass'); //得到所有类名为myclass的标签,通过下标获取对应标签,下标从0开始
document.body.querySelector("style[type='text/css'], style:not([type])");
element.querySelector('div, p')
2、创建节点
方法名 | 描述 |
---|---|
createElement() | 生成元素节点,并返回该节点 |
createTextNode() | 生成文本节点(Text实例),并返回该节点。它的参数是文本节点的内容 |
var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hello');
newDiv.appendChild(newContent);
Document对象属性和方法参考
document.body.nodeName;//'BODY'
document.body.firstChild.nodeName;//'svg'
document.body.lastChild.nodeName;'DIV'
除了Node原生属性 nodeType、nodeName、nodeValue常用到的这三个,还有一些属性经常使用,列举如下。
element属性 | 描述 |
---|---|
Element.id | 返回指定元素的id属性,该属性可读写 |
Element.title | 用来读写当前元素的 HTML 属性title。该属性通常用来指定,鼠标悬浮时弹出的文字提示框。 |
Element.dir | 用于读写当前元素的文字方向,可能是从左到右(“ltr”,默认ltr),也可能是从右到左(“rtl”) |
Element.className | 用来读写当前元素节点的class属性。它的值是一个字符串,每个class之间用空格分割。 |
Element.innerHTML | 返回一个字符串,等同于该元素包含的所有 HTML 代码。该属性可读写,常用来设置某个节点的内容。它能改写所有元素节点的内容,包括HTML和body元素。 |
Element.outerHTML | 返回一个字符串,表示当前元素节点的所有 HTML 代码,包括该元素本身和所有子元素。 |
Element.clientHeight | 元素节点的 CSS 高度,只对块级元素生效,对于行内元素返回0;还包括padding部分,但是不包括border、margin;如果有水平滚动条,还要减去水平滚动条的高度。注意,这个值始终是整数,如果是小数会被四舍五入。 |
Element.clientWidth | 返回元素节点的 CSS 宽度,同样只对块级元素有效,也是只包括元素本身的宽度和padding,如果有垂直滚动条,还要减去垂直滚动条的宽度 |
Element.id | 该属性可读写 |
Element.id | 该属性可读写 |
// HTML 代码为 <p id="foo">
var p = document.querySelector('p');
p.id // "foo"
// HTML 代码 <div class="one two three" id="myDiv"></div>
var div = document.getElementById('myDiv');
div.className;// "one two three"
//获取document中body的标题
document.body.title;//''
//修改标题
document.body.title='body标题';
//<a class="link" href="http://www.baidu.com">百度</a>
var link=document.querySelector('.link');
link.target='_blank'; //设置链接打开方式
link.title='百度';//设置title
link.href='https://www.taobao.com/'; //修改href链接
元素节点提供六个方法,用来操作属性
// HTML 代码为
//<div id="div1" align="left">div内容</div>
var div = document.getElementById('div1');
//读取属性值
console.log(div.getAttribute('align'));//获取align属性的值 "left"
//读取所有属性名
console.log(div.getAttributeNames());//获取div1标签上的所有属性 ["id", "align"]
//新增属性
div.setAttribute('title','新增标题');
console.log(div.getAttributeNames());//(3) ["id", "align", "title"]
//检查某个属性是否存在
console.log(div.hasAttribute('title'));//true
console.log(div.hasAttribute('align'));//true
console.log(div.hasAttribute('class'));//false
//判断是否有属性
console.log(div.hasAttributes());
//移除指定属性
div.removeAttribute('title');//移除title属性
//获取文本内容
// HTML 代码为
//<div id="div1" align="left">div内容</div>
console.log(div.childNodes[0]); //"div内容"
console.log(div.firstChild); //"div内容"
console.log(div.childNodes[0].nodeType); //3 文本节点
编辑Text节点文本内容的方法:
// HTML 代码为
// <p>Hello World</p>
var pElementText = document.querySelector('p').firstChild;
console.log(pElementText);
pElementText.appendData('!!!');
pElementText.deleteData(0,1);
pElementText.insertData(0, 'Hello ');
pElementText.replaceData(0,5,'替换');//从0开始替换,替换5个字符,替换为文本"替换"
console.log(pElementText.substringData(0,2));//替换
console.log(pElementText.splitText(2));//" ello World!!!"
//插入文本
var newContent = document.createTextNode('创建文本');
document.querySelector('p').appendChild(newContent);
当把节点插入到DOM树中,使用appendChild方法时,会造成浏览器的重绘(插入节点浏览器需要将DOM重新绘制出来,重新绘制的过程被称之为重绘),如果循环语句插入多个节点多次重绘会影响浏览器性能,可以通过打包,一次性插入来解决。
document.createDocumentFragment():document.createDocumentFragment方法生成一个空的文档片段对象
//<ul id="ul"></ul>
var list=['列表一','列表二','列表三','列表四','列表五']
var docfrag = document.createDocumentFragment();
list.forEach(function (e) {
var li = document.createElement('li');
li.textContent = e;
docfrag.appendChild(li);
});
var element = document.getElementById('ul');
element.appendChild(docfrag);
注意:IE浏览器,使用文档片段DocumentFragment的性能并不会更好,反而变差;chrome和firefox浏览器,使用文档片段DocumentFragment可以提升性能
DOM节点相关学习文章
注意:HTML不区分大小写,JS区分大小写,当HTML的属性需要用JS的方式表示,无论HTML中使用的大写还是小写,在js中均要转变为小写
html5中我们可以使用data-前缀设置我们需要的自定义属性,来进行一些数据的存放,例如我们在标签上设置语言
//<a href="javascript:;" data-lang="English">测试</a>
//<div data-lang="English">测试</div>
var alink=document.querySelector('a');
console.log(alink.dataset.lang);//English 获取data-lang的值
alink.setAttribute('data-lang','Chinese'); //溪公安data-lang的值
//删除自定义属性
delete alink.dataset.foo;
如果浏览器支持dataset,则会弹出注释内容;如果浏览器不支持dataset则会报错;目前在Opera 11.1+、Chrome 9+下可以通过javascript使用dataset访问你自定义的data属性。
除了dataset属性,也可以用getAttribute(‘data-lang’)、removeAttribute(‘data-lang’)、setAttribute(‘data-lang’)、hasAttribute(‘data-lang’)等方法操作data-*属性