文档对象模型 DOM
DOM 是 JavaScript 操作⽹页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将⽹页转为一个 JavaScript 对象,从而可以⽤脚本进行各种操作 (⽐如增删内容)。
DOM 的最小组成单位叫做节点(node)。⽂档的树形结构(DOM树),就是由各种不同类型的节点组成。
节点的类型有七种
浏览器提供⼀个原生的节点对象 Node ,下面这七种节点都继承了 Node ,因此具有⼀些共同的属性和⽅法。最常用的是document对象和Element对象。
- Document :整个文档树的顶层节点
- DocumentType : doctype 标签(⽐如 <!DOCTYPE html> )
- Element :⽹页的各种HTML标签(比如 <body> 、 <a> 等)
- Attribute :⽹页元素的属性(⽐如 class="right" )
- Text :标签之间或标签包含的⽂本
- Comment :注释
- DocumentFragment :⽂档的⽚段
document 对象
每个载入浏览器的HTML文档都会成为document对象。document对象表示整个HTML页面,是window对象的一个属性。
document对象常用属性
document.doctype //获取HTML文档的doctype声明,例如<!DOCTYPE html>,没有则返回null
document.title //返回当前文档的标题,该属性是可写的
document.characterSet //返回渲染当前文档的字符集,例如"UTF-8"
document.head //获取文档中被head节点包裹的内容
document.body //获取文档中被body节点包裹的内容
document.images //获取文档中所有图片
document.readyState // 返回当前文档的状态,共有三种可能的值
//1. loading:加载HTML代码阶段,尚未完成解析
//2. interactive:加载外部资源阶段
//3. complete:全部加载完成
document.compatMode 属性返回浏览器处理文档的模式,共有两种可能的值
//1. BackCompat:向后兼容模式,也就是没有添加DOCTYPE
//2. CSS1Compat:严格模式,添加了DOCTYPE
document.activeElement //返回当前文档中获得焦点的那个元素
//用户通常可以使用tab键移动焦点,使用空格键激活焦点
document.location //返回一个只读对象,提供了当前文档的URL信息
document.location === location //true
document.location === window.location //true
// 假定当前网址为http://user:passwd@www.example.com:4097/path/a.html?x=111#part1
document.location.href // "http://user:passwd@www.example.com:4097/path/a.html?x=111#part1"
document.location.protocol // "http:"
document.location.host // "www.example.com:4097"
document.location.hostname // "www.example.com"
document.location.port // "4097"
document.location.pathname // "/path/a.html"
document.location.search // "?x=111"
document.location.hash // "#part1"
document.location.user // "user"
document.location.password // "passed"
document.location.assign('http://www.google.com') //跳转到另一个网址
document.location.reload(true) // 优先从服务器重新加载
document.location.reload(false) // 优先从本地缓存重新加载(默认值)
document.location.assign('http://www.google.com') // 跳转到另一个网址,但当前文档不保留在history对象中,即无法用后退按钮,回到当前文档
document.location.toString() // 将location对象转为字符串,等价于document.location.href
//虽然location属性返回的对象是只读的,但是可以将指定的URL赋值给这个属性!网页就会自动跳转到指定网址。
document.location = 'http://www.example.com';// 即跳转至网页http://www.example.com
document.cookie //获取该文档的cookie信息,cookie是存储在客户端的文本
document.getElementById('XXX').innerText //返回元素内包含的文本内容,可写属性,在多层次的时候会按照元素由浅到深的顺序拼接其内容。
<div>
<p>
123
<span>456</span>
</p>
</div>
外层div的innerText返回内容是 "123456"
document.getElementById('XXX').innerHTML //和inerText类似,但不是返回元素的文本内容,而是返回元素的HTML结构
<div>
<p>
123
<span>456</span>
</p>
</div>
外层div的innerHTML返回内容是<p>123<span>456</span></p>
document.open(); //新建一个文档,供write方法写入内容。它实际上等于清除当前文档,重新写入内容
document.write("hello"); //向当前文档写入内容
document.write("world");
document.close(); //用于关闭open方法所新建的文档。一旦关闭,write方法就无法写入内容了
注意:
1.如果页面已经渲染完成再调用write方法,它会先调用open方法,擦除当前文档所有内容,然后再写入。
2.如果在页面渲染过程中调用write方法,并不会调用open方法。
Element对象
除了document对象,在DOM中最常用的就是Element对象了,Element对象表示HTML元素,提供了对元素标签名、子节点以及特性的访问。
Element 对象可以拥有类型为元素节点、文本节点、注释节点的子节点,DOM提供了一系列的方法可以进行元素的增、删、改、查操作。
Element对象属性
nodeName:元素标签名,还有个类似的tagName
nodeType:元素类型
className:类名
id:元素id
children:子元素列表(HTMLCollection)
childNodes:子元素列表(NodeList)
firstChild:第一个子元素
lastChild:最后一个子元素
nextSibling:下一个兄弟元素
previousSibling:上一个兄弟元素
parentNode、parentElement:父元素
用法举例:
document.getElementById("MathJax_Message").nodeName //"DIV"
查询元素 (querySelector()最佳)
getElementById()
返回匹配指定ID属性的元素节点。
var elem = document.getElementById("test");
getElementsByClassName()
返回一个类数组的对象,包括了所有class名字符合指定条件的元素
var elements = document.getElementsByClassName(names);
document.getElementsByClassName('red test');
getElementsByClassName方法的参数,可以是多个空格分隔的class名字,返回同时具有这些节点的元素。这个方法不仅可以在document对象上调用,也可以在任何元素节点上调用。
getElementsByTagName()
getElementsByTagName方法返回所有指定标签的元素(搜索范围包括本身)。这个方法不仅可以在document对象上调用,也可以在任何元素节点上调用。
var paras = document.getElementsByTagName("p");
querySelector()
querySelector方法返回匹配指定的CSS选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。
即通过querySelector()使用css选择器的方法去操作dom元素,需要注意兼容性,支持IE9及以上,对IE8部分支持
var el1 = document.querySelector(".myclass");
var el2 = document.querySelector('#myParent > [ng-click]');
注意:querySelector方法无法选中CSS伪元素。
querySelectorAll()
querySelectorAll方法返回匹配指定的CSS选择器的所有节点,返回的是NodeList类型的对象。NodeList对象不是动态集合,所以元素节点的变化无法实时反映在返回结果中。
elementList = document.querySelectorAll(selectors);
querySelectorAll方法的参数,可以是逗号分隔的多个CSS选择器,返回所有匹配其中一个选择器的元素。
var matches = document.querySelectorAll("div.note, div.alert");
上面代码返回class属性是note或alert的div元素。
创建元素
createElement(标签名)
createElement方法用来生成HTML元素节点。
var newDiv = document.createElement("div");
createElement方法的参数为元素的标签名,即元素节点的tagName属性。
如果传入大写的标签名,会被转为小写。如果参数带有尖括号(即<和>)或者是null,会报错。
createTextNode()
createTextNode方法用来生成文本节点,参数为所要生成的文本节点的内容。
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
上面代码新建一个div节点和一个文本节点
createDocumentFragment()
createDocumentFragment方法生成一个DocumentFragment对象。
var docFragment = document.createDocumentFragment();
DocumentFragment对象是一个存在于内存的DOM片段,但是不属于当前文档,常常用来生成较复杂的DOM结构,然后插入当前文档。这样做的好处在于,因为DocumentFragment不属于当前文档,对它的任何改动,都不会引发网页的重新渲染,比直接修改当前文档的DOM有更好的性能表现。
修改元素&删除元素&clone元素
appendChild()
接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点。
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
newDiv.appendChild(newContent);
上面代码将新建的文本节点插入到新建的div节点的尾部。
var p = document.createElement('p');
document.body.appendChild(p);
上面代码新建一个<p>节点,将其插入document.body的尾部。
如果appendChild方法的参数是DocumentFragment节点,那么插入的是DocumentFragment的所有子节点,而不是DocumentFragment节点本身。返回值是一个空的DocumentFragment节点。
insertBefore()
insertBefore方法用于将某个节点插入父节点内部的指定位置。
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
insertBefore方法接受两个参数,第一个参数是所要插入的节点newNode,第二个参数是父节点parentNode内部的一个子节点referenceNode。newNode将插在referenceNode这个子节点的前面。返回值是插入的新节点newNode。
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
newDiv.insertBefore(newContent, newDiv.firstChild);
newContent将插在newDiv.firstChild的前面
如果insertBefore方法的第二个参数为null,则新节点将插在当前节点内部的最后位置,即变成最后一个子节点。
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
newDiv.insertBefore(newContent, null);
由于不存在insertAfter方法,如果新节点要插在父节点的某个子节点后面,可以用insertBefore方法结合nextSibling属性模拟。
parent.insertBefore(s1, s2.nextSibling);
replaceChild()替换指定元素
replaceChild()接受两个参数:要插入的元素和要替换的元素,返回值是替换走的那个节点oldElement。
newDiv.replaceChild(newElement, oldElement);
removeChild()移除子节点
接受一个子节点作为参数,用于从当前节点移除该子节点。返回值是移除的子节点。
parentNode.removeChild(childNode);
Node.hasChildNodes()判断是否有子节点
hasChildNodes方法返回一个布尔值,表示当前节点是否有子节点。
var foo = document.getElementById('foo');
if (foo.hasChildNodes()) {
foo.removeChild(foo.childNodes[0]);
}
上面代码表示,如果foo节点有子节点,就移除第一个子节点。
cloneNode()克隆一个节点
它接受一个布尔值作为参数,表示是否同时克隆子节点,false的时候只复制元素本身。它的返回值是一个克隆出来的新节点。
var cloneUL = document.querySelector('ul').cloneNode(true);
该方法有一些使用注意点。
- 克隆一个节点,会拷贝该节点的所有属性,但是会丧失addEventListener方法和on-属性(即node.onclick = fn),添加在这个节点上的事件回调函数。
- 该方法返回的节点不在文档之中,即没有任何父节点,必须使用诸如Node.appendChild这样的方法添加到文档之中。
- 克隆一个节点之后,DOM 有可能出现两个有相同id属性(即id="xxx")的网页元素,这时应该修改其中一个元素的id属性。如果原节点有name属性,可能也需要修改。
contains()判断是否包含和这个节点
document.body.contains(node)
返回一个布尔值,表示参数节点是否满足以下三个条件之一。
- 参数节点为当前节点。
- 参数节点为当前节点的子节点。
- 参数节点为当前节点的后代节点。
document.body.contains(node)
上面代码检查参数节点node,是否包含在当前文档之中。
HTMLCollection 和 NodeList
节点都是单个对象,有时需要一种数据结构,能够容纳多个节点。DOM 提供两种节点集合,用于容纳多个节点:NodeList和HTMLCollection。
NodeList 对象代表一个有顺序的节点列表,HTMLCollection 是一个接口,表示 HTML 元素的集合,它提供了可以遍历列表的方法和属性。都是类数组对象, NodeList 有 forEach 方法,而 HTMLCollection 没有。
以下方法获取HTMLCollection对象实例
document.images //所有img元素
document.links //所有带href属性的a元素和area元素
document.forms //所有form元素
document.scripts //所有script元素
document.body.children
document.getElementsByClassName("class1")
以下方法获取NodeList对象实例
Node.childNodes
document.querySelectorAll()
document.getElementsByTagName()
document.getElementsByName("name1")
document.body.childNodes instanceof NodeList // true
NodeList实例是一个类数组对象,具有length属性和forEach方法(也可以用for遍历),可以使用方括号运算符取出成员。但不能使用pop或push之类数组特有的方法。
var children = document.body.childNodes;
Array.isArray(children) // false
children.length // 34
children.forEach(console.log)
document.body.childNodes[0] //返回第一个成员
如果NodeList实例要使用数组方法,可以使用Array.prototype.slice.call()将其转为真正的数组。
var children = document.body.childNodes;
var nodeArr = Array.prototype.slice.call(children);
注意:NodeList 实例可能是动态集合,也可能是静态集合。所谓动态集合就是一个活的集合,DOM 删除或新增一个相关节点,都会立刻反映在 NodeList 实例。目前,只有Node.childNodes返回的是一个动态集合,其他的 NodeList 都是静态集合。
var children = document.body.childNodes;
children.length // 18
document.body.appendChild(document.createElement('p'));
children.length // 19
上面代码中,文档增加一个子节点,NodeList 实例children的length属性就增加了1。
NodeList.prototype.length
length属性返回NodeList 实例包含的节点数量。
document.getElementsByTagName('xxx').length
NodeList.prototype.forEach()
forEach方法用于遍历 NodeList 的所有成员。它接受一个回调函数作为参数,每一轮遍历就执行一次这个回调函数,用法与数组实例的forEach方法完全一致。
var children = document.body.childNodes;
children.forEach(function(item,i,list){})
回调函数的三个参数依次是当前元素、当前元素索引值、整个NodeList 实例
NodeList.prototype.keys(),NodeList.prototype.values(),NodeList.prototype.entries()
keys()返回键名的遍历器,values()返回键值的遍历器,entries()返回的遍历器同时包含键名和键值的信息。都可以通过for...of循环遍历获取每一个成员的信息。
var children = document.body.childNodes;
for (var key of children.keys()) {
console.log(key);
}
// 0
// 1
// 2
// ...
for (var value of children.values()) {
console.log(value);
}
// #text
// <script>
// ...
for (var entry of children.entries()) {
console.log(entry);
}
// Array [ 0, #text ]
// Array [ 1, <script> ]
// ...
属性操作
getAttribute()获取元素的attribute值(一个标签的属性名和属性值可以自己定义)
var node = document.querySelector('p')
node.getAttribute('id');
setAttribute(属性名,属性值) 设置元素属性
var node = document.getElementById("div1");
node.setAttribute("my_attrib", "newVal");
romoveAttribute() 删除元素属性
node.removeAttribute('id');
常见使用方式
样式的改变建议使用 class 的新增删除来实现
Element.classList方法: 操作元素的class
var nodeBox = document.querySelector('.box')
console.log( nodeBox.classList )
nodeBox.classList.add('active') //新增 class
nodeBox.classList.remove('active') //删除 class
nodeBox.classList.toggle('active') //新增/删除切换(即如果类存在,则删除它并返回false,如果不存在,则添加它并返回true。)
node.classList.contains('active') // 判断是否存在指定的class
Element.classList.item ( Number ) //按集合中的索引返回class值
页面宽高
网页可见区域宽: document.body.clientWidth;
网页可见区域高: document.body.clientHeight;
网页可见区域宽: document.body.offsetWidth (包括边线的宽);
网页可见区域高: document.body.offsetHeight (包括边线的宽);
image
image
网页正文全文宽: document.body.scrollWidth;
网页正文全文高: document.body.scrollHeight;
网页被卷去的高: document.body.scrollTop;
网页被卷去的左: document.body.scrollLeft;
网页正文部分上: window.screenTop;
网页正文部分左: window.screenLeft;
屏幕分辨率的高: window.screen.height;
屏幕分辨率的宽: window.screen.width;
屏幕可用工作区高度: window.screen.availHeight;
屏幕可用工作区宽度:window.screen.availWidth;
Element.scrollHeight: 只读属性,是一个元素内容高度的度量
Element.scrollTop:获取或设置一个元素的内容垂直滚动的像素数
scrollWidth:获取对象的滚动宽度
offsetHeight:获取对象相对于版面或由父坐标 offsetParent 属性指定的父坐标的高度
offsetTop:获取对象相对于版面或由 offsetTop 属性指定的父坐标的计算顶端位置
event.clientX 相对文档的水平座标
event.clientY 相对文档的垂直座标
event.offsetX 相对容器的水平坐标
event.offsetY 相对容器的垂直坐标
event.clientX+document.documentElement.scrollTop 相对文档的水平座标+垂直方向滚动的量
网友评论