DOM模型这个特别重要哦(二)
三、NodeList 接口 与 HTMLCollection 接口 (节点的集合)
节点都是单个对象,有时需要一种数据结构,能够容纳多个节点。DOM 提供两种节点集合,用于容纳多个节点:NodeList
和HTMLCollection
。
这两种集合都属于接口规范。许多 DOM 属性和方法,返回的结果是NodeList
实例或HTMLCollection
实例。主要区别是,NodeList
可以包含各种类型的节点,HTMLCollection
只能包含 HTML 元素节点。
1、NodeList 接口 (各类节点的集合)
1.1 概述
NodeList
实例是一个类似数组的对象,它的成员是节点对象。通过以下方法可以得到NodeList
实例。
Node.childNodes
属性document.querySelectorAll()
等节点搜索方法
document.body.childNodes instanceof NodeList // true
/*
<ul>
<li></li>
<li></li>
<li></li>
</ul>*/
var $li = document.querySelectorAll('li');
console.log($li) // NodeList(3) [li, li, li]
console.log($li instanceof NodeList) // true
NodeList
实例很像数组,可以使用length
属性和forEach
方法。但是,它不是数组,不能使用pop
或push
之类数组特有的方法。
var children = document.body.childNodes;
Array.isArray(children) // false
children.length // 34
children.forEach(console.log)
上面代码中,NodeList 实例children
不是数组,但是具有length
属性和forEach
方法。
如果NodeList
实例要使用数组方法,可以将其转为真正的数组。
var children = document.body.childNodes;
var nodeArr = Array.prototype.slice.call(children);
除了使用forEach
方法遍历 NodeList 实例,还可以使用for
循环。
var children = document.body.childNodes;
for (var i = 0; i < children.length; i++) {
var item = children[i];
}
注意,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。
1.2 NodeList.prototype.length
length
属性返回 NodeList 实例包含的节点数量。
document.querySelectorAll('xxx').length
// 0
上面代码中,document.querySelectorAll
返回一个 NodeList 集合。对于那些不存在的 HTML 标签,length
属性返回0
。
1.3 NodeList.prototype.forEach()
forEach
方法用于遍历 NodeList 的所有成员。它接受一个回调函数作为参数,每一轮遍历就执行一次这个回调函数,用法与数组实例的forEach
方法完全一致。
var children = document.body.childNodes;
children.forEach(function f(item, i, list) { // 参数:当前成员、当前索引、NodeList实例
// ...
}, this); // forEach参数二: 绑定回调函数内部的this,可省略
上面代码中,回调函数f
的三个参数依次是当前成员、位置和当前 NodeList 实例。forEach
方法的第二个参数,用于绑定回调函数内部的this
,该参数可省略。
1.4 NodeList.prototype.item() 接受一个索引参数,返回该索引上的成员(节点)
item
方法接受一个整数值作为参数,表示成员的位置,返回该位置上的成员。
document.body.childNodes.item(0)
上面代码中,item(0)
返回第一个成员。
如果参数值大于实际长度,或者索引不合法(比如负数),item
方法返回null
。如果省略参数,item
方法会报错。
所有类似数组的对象,都可以使用方括号运算符取出成员。一般情况下,都是使用方括号运算符,而不使用item
方法。
document.body.childNodes[0]
1.5 NodeList.prototype.keys(),NodeList.prototype.values(),NodeList.prototype.entries() 遍历器对象,结合for...of遍历每一个成员的信息
这三个方法都返回一个 ES6 的遍历器对象,可以通过for...of
循环遍历获取每一个成员的信息。区别在于,keys()
返回键名的遍历器,values()
返回键值的遍历器,entries()
返回的遍历器同时包含键名和键值的数组。
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> ]
// ...
2、HTMLCollection 接口 (HTML元素节点的集合)
2.1 概述
HTMLCollection
是一个节点对象的集合,只能包含元素节点(element),不能包含其他类型的节点。它的返回值是一个类似数组的对象,但是与NodeList
接口不同,HTMLCollection
没有forEach
方法,只能使用for
循环遍历。
返回HTMLCollection
实例的,主要是一些Document
对象的集合属性,比如document.links
、document.forms
、document.images
等。
document.links instanceof HTMLCollection // true
HTMLCollection
实例都是动态集合,节点的变化会实时反映在集合中。
如果元素节点有id
或name
属性,那么HTMLCollection
实例上面,可以使用id
属性或name
属性引用该节点元素。如果没有对应的节点,则返回null
。
// HTML 代码如下
// <img id="pic" src="http://example.com/foo.jpg">
var pic = document.getElementById('pic');
document.images.pic === pic // true
上面代码中,document.images
是一个HTMLCollection
实例,可以通过<img>
元素的id
属性值,从HTMLCollection
实例上取到这个元素。
各js选择器返回的数据对比
<ul>
<li id="li1"></li>
<li class="li2"></li>
<li name='liName'></li>
</ul>
<script>
var li1 = document.getElementById('li1');
var li2 = document.querySelector('#li1');
var li3 = document.getElementsByClassName('li2');
var li4 = document.getElementsByTagName('li');
var li5 = document.getElementsByName('liName');
var li6 = document.querySelectorAll('li');
console.log(li1) // <li id="li1"></li>
console.log(li2) // <li id="li1"></li>
console.log(li3) // HTMLCollection [li.li2]
console.log(li4) // HTMLCollection(3) [li#li1, li.li2, li, li1: li#li1, liName: li]
console.log(li5) // NodeList [li]
console.log(li6) // NodeList(3) [li#li1, li.li2, li]
</script>
2.2 HTMLCollection.prototype.length
length
属性返回HTMLCollection
实例包含的成员数量。
document.links.length // 18
2.3 HTMLCollection.prototype.item() 和方括号访问一样
item
方法接受一个整数值作为参数,表示成员的位置,返回该位置上的成员。
var c = document.images;
var img0 = c.item(0);
上面代码中,item(0)
表示返回0号位置的成员。由于方括号运算符也具有同样作用,而且使用更方便,所以一般情况下,总是使用方括号运算符。
如果参数值超出成员数量或者不合法(比如小于0),那么item
方法返回null
。
2.4 HTMLCollection.prototype.namedItem() 参数为id或name,返回元素节点
namedItem
方法的参数是一个字符串,表示id
属性或name
属性的值,返回对应的元素节点。如果没有对应的节点,则返回null
。
// HTML 代码如下
// <img id="pic" src="http://example.com/foo.jpg">
var pic = document.getElementById('pic');
document.images.namedItem('pic') === pic // true
四、ParentNode 接口(父节点接口),ChildNode 接口(子节点接口)
节点对象除了继承 Node 接口以外,还会继承其他接口。ParentNode
接口表示当前节点是一个父节点,提供一些处理子节点的方法。ChildNode
接口表示当前节点是一个子节点,提供一些相关方法。
1、ParentNode 接口
如果当前节点是父节点,就会继承ParentNode
接口。由于只有元素节点(element)、文档节点(document)和文档片段节点(documentFragment)拥有子节点,因此只有这三类节点会继承ParentNode
接口。
1.1 属性
(1)ParentNode.children (返回HTMLCollection实例,当前节点的所有元素子节点)
children
属性返回一个HTMLCollection
实例,成员是当前节点的所有元素子节点。该属性只读。
下面是遍历某个节点的所有元素子节点的示例。
for (var i = 0; i < el.children.length; i++) {
// ...
}
注意,children
属性只包括元素子节点,不包括其他类型的子节点(比如文本子节点)。如果没有元素类型的子节点,返回值HTMLCollection
实例的length
属性为0
。
另外,HTMLCollection
是动态集合,会实时反映 DOM 的任何变化。
(2)ParentNode.firstElementChild 第一个元素子节点
firstElementChild
属性返回当前节点的第一个元素子节点。如果没有任何元素子节点,则返回null
。
document.firstElementChild.nodeName
// "HTML"
上面代码中,document
节点的第一个元素子节点是``。
(3)ParentNode.lastElementChild 最后一个元素子节点
lastElementChild
属性返回当前节点的最后一个元素子节点,如果不存在任何元素子节点,则返回null
。
document.lastElementChild.nodeName
// "HTML"
上面代码中,document
节点的最后一个元素子节点是``(因为document
只包含这一个元素子节点)。
(4)ParentNode.childElementCount 所有元素子节点的数量
childElementCount
属性返回一个整数,表示当前节点的所有元素子节点的数目。如果不包含任何元素子节点,则返回0
。
document.body.childElementCount // 13
1.2 方法
ParentNode.append() 插入子节点于后,ParentNode.prepend() 插入子节点于前
append
方法为当前节点追加一个或多个子节点,位置是最后一个元素子节点的后面。
该方法不仅可以添加元素子节点,还可以添加文本子节点。
var parent = document.body;
// 添加元素子节点
var p = document.createElement('p');
parent.append(p);
// 添加文本子节点
parent.append('Hello');
// 添加多个元素子节点
var p1 = document.createElement('p');
var p2 = document.createElement('p');
parent.append(p1, p2);
// 添加元素子节点和文本子节点
var p = document.createElement('p');
parent.append('Hello', p);
注意,该方法没有返回值。
prepend
方法为当前节点追加一个或多个子节点,位置是第一个元素子节点的前面。它的用法与append
方法完全一致,也是没有返回值。
2、ChildNode 接口
如果一个节点有父节点,那么该节点就继承了ChildNode
接口。
2.1 ChildNode.remove() 移除当前节点
remove
方法用于从父节点移除当前节点。
el.remove()
上面代码在 DOM 里面移除了el
节点。
2.2 ChildNode.before() 插入同级节点于前,ChildNode.after() 插入同级节点于后
before
方法用于在当前节点的前面,插入一个或多个同级节点。两者拥有相同的父节点。
注意,该方法不仅可以插入元素节点,还可以插入文本节点。
var p = document.createElement('p');
var p1 = document.createElement('p');
// 插入元素节点
el.before(p);
// 插入文本节点
el.before('Hello');
// 插入多个元素节点
el.before(p, p1);
// 插入元素节点和文本节点
el.before(p, 'Hello');
after
方法用于在当前节点的后面,插入一个或多个同级节点,两者拥有相同的父节点。用法与before
方法完全相同。
2.3 ChildNode.replaceWith() 用参数节点替换当前节点
replaceWith
方法**使用参数节点,替换当前节点。**参数可以是元素节点,也可以是文本节点。
var span = document.createElement('span');
el.replaceWith(span);
上面代码中,el
节点将被span
节点替换。
五、Document节点
1、概述
document
节点对象代表整个文档,每张网页都有自己的document
对象。window.document
属性就指向这个对象。只要浏览器开始载入 HTML 文档,该对象就存在了,可以直接使用。
document
对象有不同的办法可以获取。
- 正常的网页,直接使用
document
或window.document
。 iframe
框架里面的网页,使用iframe
节点的contentDocument
属性。- Ajax 操作返回的文档,使用
XMLHttpRequest
对象的responseXML
属性。 - 内部节点的
ownerDocument
属性。
document
对象继承了EventTarget
接口、Node
接口、ParentNode
接口。这意味着,这些接口的方法都可以在document
对象上调用。除此之外,document
对象还有很多自己的属性和方法。
2、属性
2.1 快捷方式属性 (指向文档内部某个节点的快捷方式)
以下属性是指向文档内部的某个节点的快捷方式。
(1)document.defaultView 返回window对象
document.defaultView
属性返回document
对象所属的window
对象。如果当前文档不属于window
对象,该属性返回null
。
document.defaultView === window // true
(2)document.doctype 指向<!DOCTYPE html>
对于 HTML 文档来说,document
对象一般有两个子节点。第一个子节点是document.doctype
,指向<DOCTYPE>
节点,即文档类型(Document Type Declaration,简写DTD)节点。HTML 的文档类型节点,一般写成<!DOCTYPE html>
。如果网页没有声明 DTD,该属性返回null
。
var doctype = document.doctype;
doctype // "<!DOCTYPE html>"
doctype.name // "html"
document.firstChild
通常就返回这个节点。
(3)document.documentElement 返回当前文档的根元素节点,即<html>节点
document.documentElement
属性返回当前文档的根元素节点(root)。它通常是document
节点的第二个子节点,紧跟在document.doctype
节点后面。HTML网页的该属性,一般是<html>
节点。
(4)document.body 指向<body>,document.head 指向 <head>
document.body
属性指向<body>
节点,document.head
属性指向<head>
节点。
这两个属性总是存在的,如果网页源码里面省略了<head>
或<body>
,浏览器会自动创建。另外,这两个属性是可写的,如果改写它们的值,相当于移除所有子节点。
(5)document.scrollingElement 返回文档的滚动元素(标准模式<html>,兼容模式<body>)
document.scrollingElement
属性返回文档的滚动元素。也就是说,当文档整体滚动时,到底是哪个元素在滚动。
标准模式下,这个属性返回的文档的根元素document.documentElement
(即<html>
)。
兼容(quirk)模式下,返回的是<body>
元素,如果该元素不存在,返回null
。
// 页面滚动到浏览器顶部
document.scrollingElement.scrollTop = 0;
(6)document.activeElement 返回文档中获取焦点的元素,一般为表单元素,如没有则返回<body>或null
document.activeElement
属性返回获得当前焦点(focus)的 DOM 元素。通常,这个属性返回的是<input>
、<textarea>
、<select>
等表单元素,如果当前没有焦点元素,返回<body>
元素或null
。
(7)document.fullscreenElement 返回当前以全屏状态展示的 DOM 元素
document.fullscreenElement
属性返回当前以全屏状态展示的 DOM 元素。如果不是全屏状态,该属性返回null
。
if (document.fullscreenElement.nodeName == 'VIDEO') {
console.log('全屏播放视频');
}
上面代码中,通过document.fullscreenElement
可以知道<video>
元素有没有处在全屏状态,从而判断用户行为。
2.2 节点集合属性 (文档内部特定元素的集合,返HTMLCollection实例)
以下属性返回一个HTMLCollection
实例,表示文档内部特定元素的集合。这些集合都是动态的,原节点有任何变化,立刻会反映在集合中。
(1)document.links 返回所有含href属性的<a>和<area>
document.links
属性返回当前文档所有设定了href
属性的<a>
及<area>
节点。
// 打印文档所有的链接
var links = document.links;
for(var i = 0; i < links.length; i++) {
console.log(links[i]);
}
(2)document.forms 返回所有<form> 表单节点
document.forms
属性返回所有<form>
表单节点。
var selectForm = document.forms[0];
上面代码获取文档第一个表单。
除了使用位置序号,id
属性和name
属性也可以用来引用表单。
/* HTML 代码如下
<form name="foo" id="bar"></form>
*/
document.forms[0] === document.forms.foo // true
document.forms.bar === document.forms.foo // true
(3)document.images 返回页面所有<img>
图片节点
document.images
属性返回页面所有<img>
图片节点。
var imglist = document.images;
for(var i = 0; i < imglist.length; i++) {
if (imglist[i].src === 'banner.gif') {
// ...
}
}
上面代码在所有img
标签中,寻找某张图片。
(4)document.embeds,document.plugins 返回所有<embed>
嵌入节点
document.embeds
属性和document.plugins
属性,都返回所有<embed>
节点。
(5)document.scripts 返回所有<script>
节点
document.scripts
属性返回所有<script>
节点。
var scripts = document.scripts;
if (scripts.length !== 0 ) {
console.log('当前网页有脚本');
}
(6)document.styleSheets 返回文档内嵌或引入的样式表集合
document.styleSheets
属性返回文档内嵌或引入的样式表集合,详细介绍请看《CSS 操作》一章。
(7)小结(除了styleSheets属性,其他返回都是HTMLCollection实例)
除了document.styleSheets
,以上的集合属性返回的都是HTMLCollection
实例。
document.links instanceof HTMLCollection // true
document.images instanceof HTMLCollection // true
document.forms instanceof HTMLCollection // true
document.embeds instanceof HTMLCollection // true
document.scripts instanceof HTMLCollection // true
HTMLCollection
实例是类似数组的对象,所以这些属性都有length
属性,都可以使用方括号运算符引用成员。如果成员有id
或name
属性,还可以用这两个属性的值,在HTMLCollection
实例上引用到这个成员。
// HTML 代码如下
// <form name="myForm">
document.myForm === document.forms.myForm // true
2.3 文档静态信息属性
以下属性返回文档信息。
(1)document.documentURI,document.URL 返回网址字符串
document.documentURI
属性和document.URL
属性都返回一个字符串,表示当前文档的网址。不同之处是它们继承自不同的接口,documentURI
继承自Document
接口,可用于所有文档;URL
继承自HTMLDocument
接口,只能用于 HTML 文档。
document.URL
// http://www.example.com/about
document.documentURI === document.URL
// true
如果文档的锚点(#anchor
)变化,这两个属性都会跟着变化。
(2)document.domain 返回域名
document.domain
属性返回当前文档的域名,不包含协议和端口。比如,网页的网址是http://www.example.com:80/hello.html
,那么document.domain
属性就等于www.example.com
。如果无法获取域名,该属性返回null
。
document.domain
基本上是一个只读属性,只有一种情况除外。次级域名的网页,可以把document.domain
设为对应的上级域名。比如,当前域名是a.sub.example.com
,则document.domain
属性可以设置为sub.example.com
,也可以设为example.com
。修改后,document.domain
相同的两个网页,可以读取对方的资源,比如设置的 Cookie。
另外,设置document.domain
会导致端口被改成null
。因此,如果通过设置document.domain
来进行通信,双方网页都必须设置这个值,才能保证端口相同。
(3)document.location 提供 URL 相关的信息和操作方法
Location
对象是浏览器提供的原生对象,提供 URL 相关的信息和操作方法。通过window.location
和document.location
属性,可以拿到这个对象。
关于这个对象的详细介绍,请看《浏览器模型》部分的《Location 对象》章节。
(4)document.lastModified 返回当前文档最后修改的时间
document.lastModified
属性返回一个字符串,表示当前文档最后修改的时间。不同浏览器的返回值,日期格式是不一样的。
document.lastModified
// "03/07/2018 11:18:27"
注意,document.lastModified
属性的值是字符串,所以不能直接用来比较。Date.parse
方法将其转为Date
实例,才能比较两个网页。
var lastVisitedDate = Date.parse('01/01/2018');
if (Date.parse(document.lastModified) > lastVisitedDate) {
console.log('网页已经变更');
}
如果页面上有 JavaScript 生成的内容,document.lastModified
属性返回的总是当前时间。
(5)document.title 返回当前文档的标题
document.title
属性返回当前文档的标题。默认情况下,返回<title>
节点的值。但是该属性是可写的,一旦被修改,就返回修改后的值。
document.title = '新标题';
document.title // "新标题"
(6)document.characterSet 返回当前文档的编码,如UTF-8等
document.characterSet
属性返回当前文档的编码,比如UTF-8
、ISO-8859-1
等等。
(7)document.referrer 当前文档的访问者来自哪里(如通过百度进入的)
document.referrer
属性返回一个字符串,表示当前文档的访问者来自哪里。
document.referrer
// "https://example.com/path"
如果无法获取来源,或者用户直接键入网址而不是从其他网页点击进入,document.referrer
返回一个空字符串。
document.referrer
的值,总是与 HTTP 头信息的Referer
字段保持一致。但是,document.referrer
的拼写有两个r
,而头信息的Referer
字段只有一个r
。
(8)document.dir 返回文字方向(rtl从右到左,ltr从左到右)
document.dir
返回一个字符串,表示文字方向。它只有两个可能的值:rtl
表示文字从右到左,阿拉伯文是这种方式;ltr
表示文字从左到右,包括英语和汉语在内的大多数文字采用这种方式。
注: 第一次调用该属性时,可能返回空字符串""
(9)document.compatMode 返回浏览器处理文档的模式(向后兼容与严格模式)
compatMode
属性返回浏览器处理文档的模式,可能的值为BackCompat
(向后兼容模式)和CSS1Compat
(严格模式)。
一般来说,如果网页代码的第一行设置了明确的DOCTYPE
(比如``),document.compatMode
的值都为CSS1Compat
。
2.4 文档状态属性
(1)document.hidden 当前页面是否可见,返布尔值。
document.hidden
属性返回一个布尔值,表示当前页面是否可见。如果窗口最小化、浏览器切换了 Tab,都会导致导致页面不可见,使得document.hidden
返回true
。
这个属性是 Page Visibility API 引入的,一般都是配合这个 API 使用。
(2)document.visibilityState 返回文档可见状态(visible、hidden、prerender、unloaded)
document.visibilityState
返回文档的可见状态。
它的值有四种状态。
visible
:页面可见。注意,页面可能是部分可见,即不是焦点窗口,前面被其他窗口部分挡住了。hidden
:页面不可见,有可能窗口最小化,或者浏览器切换到了另一个 Tab。prerender
:页面处于正在渲染状态,对于用户来说,该页面不可见。unloaded
:页面从内存里面卸载了。
这个属性可以用在页面加载时,防止加载某些资源;或者页面不可见时,停掉一些页面功能。
(3)document.readyState 返回当前文档的状态(loading、interactive、complete)
document.readyState
属性返回当前文档的状态,共有三种可能的值。
loading
:加载 HTML 代码阶段(尚未完成解析)interactive
:加载外部资源阶段complete
:加载完成
这个属性变化的过程如下。
- 浏览器开始解析 HTML 文档,
document.readyState
属性等于loading
。 - 浏览器遇到 HTML 文档中的
<script>
元素,并且没有async
或defer
属性,就暂停解析,开始执行脚本,这时document.readyState
属性还是等于loading
。 - HTML 文档解析完成,
document.readyState
属性变成interactive
。 - 浏览器等待图片、样式表、字体文件等外部资源加载完成,一旦全部加载完成,
document.readyState
属性变成complete
。
下面的代码用来检查网页是否加载成功。
// 基本检查
if (document.readyState === 'complete') {
// ...
}
// 轮询检查
var interval = setInterval(function() {
if (document.readyState === 'complete') {
clearInterval(interval);
// ...
}
}, 100);
另外,每次状态变化都会触发一个readystatechange
事件。
2.5 document.cookie 用来操作浏览器 Cookie
document.cookie
属性用来操作浏览器 Cookie,详见《浏览器模型》部分的《Cookie》章节。
2.6 document.designMode 控制当前文档是否可编辑(on、off)
document.designMode
属性控制当前文档是否可编辑。该属性只有两个值on
和off
,默认值为off
。一旦设为on
,用户就可以编辑整个文档的内容。
下面代码打开iframe
元素内部文档的designMode
属性,就能将其变为一个所见即所得的编辑器。
// HTML 代码如下
// <iframe id="editor" src="about:blank"></iframe>
var editor = document.getElementById('editor');
editor.contentDocument.designMode = 'on';
2.7 document.implementation 返回一个DOMImplementation
对象
document.implementation
属性返回一个DOMImplementation
对象。该对象有三个方法,主要用于创建独立于当前文档的新的 Document 对象。
DOMImplementation.createDocument()
:创建一个 XML 文档。DOMImplementation.createHTMLDocument()
:创建一个 HTML 文档。DOMImplementation.createDocumentType()
:创建一个 DocumentType 对象。
下面是创建 HTML 文档的例子。
var doc = document.implementation.createHTMLDocument('Title');
var p = doc.createElement('p');
p.innerHTML = 'hello world';
doc.body.appendChild(p);
document.replaceChild(
doc.documentElement,
document.documentElement
);
上面代码中,第一步生成一个新的 HTML 文档doc
,然后用它的根元素document.documentElement
替换掉document.documentElement
。这会使得当前文档的内容全部消失,变成hello world
。
3、方法
3.1 document.open(),document.close() 打开和关闭文档可写状态
document.open
方法清除当前文档所有内容,使得文档处于可写状态,供document.write
方法写入内容。
document.close
方法用来关闭document.open()
打开的文档。
document.open();
document.write('hello world');
document.close();
3.2 document.write() 向当前文档写入内容,document.writeln() 写入内容并换行
document.write
方法用于向当前文档写入内容。
在网页的首次渲染阶段,只要页面没有关闭写入(即没有执行document.close()
),document.write
写入的内容就会追加在已有内容的后面。
// 页面显示“helloworld”
document.open();
document.write('hello');
document.write('world');
document.close();
注意,document.write
会当作 HTML 代码解析,不会转义。
document.write('<p>hello world</p>');
上面代码中,document.write
会将<p>
当作 HTML 标签解释。
如果页面已经解析完成(DOMContentLoaded
事件发生之后),再调用write
方法,它会先调用open
方法,擦除当前文档所有内容,然后再写入。
document.addEventListener('DOMContentLoaded', function (event) {
document.write('<p>Hello World!</p>');
});
// 等同于
document.addEventListener('DOMContentLoaded', function (event) {
document.open();
document.write('<p>Hello World!</p>');
document.close();
});
如果在页面渲染过程中调用write
方法,并不会自动调用open
方法。(可以理解成,open
方法已调用,但close
方法还未调用。)
<html>
<body>
hello
<script type="text/javascript">
document.write("world")
</script>
</body>
</html>
在浏览器打开上面网页,将会显示hello world
。
document.write
是 JavaScript 语言标准化之前就存在的方法,现在完全有更符合标准的方法向文档写入内容(比如对innerHTML
属性赋值)。所以,除了某些特殊情况,应该尽量避免使用document.write
这个方法。
document.writeln
方法与write
方法完全一致,除了会在输出内容的尾部添加换行符。
document.write(1);
document.write(2);
// 12
document.writeln(1);
document.writeln(2);
// 1
// 2
//
注意,writeln
方法添加的是 ASCII 码的换行符,渲染成 HTML 网页时不起作用,即在网页上显示不出换行。网页上的换行,必须显式写入
。
3.3 document.querySelector() 返回匹配该选择器的元素节点,document.querySelectorAll() 返回一个NodeList
对象,包含所有匹配给定选择器的节点
document.querySelector
方法接受一个 CSS 选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null
。
var el1 = document.querySelector('.myclass');
var el2 = document.querySelector('#myParent > [ng-click]');
document.querySelectorAll
方法与querySelector
用法类似,区别是返回一个NodeList
对象,包含所有匹配给定选择器的节点。
elementList = document.querySelectorAll('.myclass');
这两个方法的参数,可以是逗号分隔的多个 CSS 选择器,返回匹配其中一个选择器的元素节点,这与 CSS 选择器的规则是一致的。
var matches = document.querySelectorAll('div.note, div.alert');
上面代码返回class
属性是note
或alert
的div
元素。
这两个方法都支持复杂的 CSS 选择器。
// 选中 data-foo-bar 属性等于 someval 的元素
document.querySelectorAll('[data-foo-bar="someval"]');
// 选中 myForm 表单中所有不通过验证的元素
document.querySelectorAll('#myForm :invalid');
// 选中div元素,那些 class 含 ignore 的除外
document.querySelectorAll('DIV:not(.ignore)');
// 同时选中 div,a,script 三类元素
document.querySelectorAll('DIV, A, SCRIPT');
但是,它们不支持 CSS 伪元素的选择器(比如:first-line
和:first-letter
)和伪类的选择器(比如:link
和:visited
),即无法选中伪元素和伪类。
如果querySelectorAll
方法的参数是字符串*
,则会返回文档中的所有元素节点。另外,querySelectorAll
的返回结果不是动态集合,不会实时反映元素节点的变化。
最后,这两个方法除了定义在document
对象上,还定义在元素节点上,即在元素节点上也可以调用。
3.4 document.getElementsByTagName() 搜索 HTML 标签名,返回符合条件的元素
document.getElementsByTagName
方法搜索 HTML 标签名,返回符合条件的元素。它的返回值是一个类似数组对象(HTMLCollection
实例),可以实时反映 HTML 文档的变化。如果没有任何匹配的元素,就返回一个空集。
var paras = document.getElementsByTagName('p');
paras instanceof HTMLCollection // true
上面代码返回当前文档的所有p
元素节点。
HTML 标签名是大小写不敏感的,因此getElementsByTagName
方法也是大小写不敏感的。另外,返回结果中,各个成员的顺序就是它们在文档中出现的顺序。
如果传入*
,就可以返回文档中所有 HTML 元素。
var allElements = document.getElementsByTagName('*');
注意,元素节点本身也定义了getElementsByTagName
方法,返回该元素的后代元素中符合条件的元素。也就是说,这个方法不仅可以在document
对象上调用,也可以在任何元素节点上调用。
var firstPara = document.getElementsByTagName('p')[0];
var spans = firstPara.getElementsByTagName('span');
上面代码选中第一个p
元素内部的所有span
元素。
3.5 document.getElementsByClassName() 返回class符合条件的元素
document.getElementsByClassName
方法返回一个类似数组的对象(HTMLCollection
实例),包括了所有class
名字符合指定条件的元素,元素的变化实时反映在返回结果中。
var elements = document.getElementsByClassName(names);
由于class
是保留字,所以 JavaScript 一律使用className
表示 CSS 的class
。
参数可以是多个class
,它们之间使用空格分隔。
var elements = document.getElementsByClassName('foo bar');
上面代码返回同时具有foo
和bar
两个class
的元素,foo
和bar
的顺序不重要。
注意,正常模式下,CSS 的class
是大小写敏感的。(quirks mode
下,大小写不敏感。)
与getElementsByTagName
方法一样,getElementsByClassName
方法不仅可以在document
对象上调用,也可以在任何元素节点上调用。
// 非document对象上调用
var elements = rootElement.getElementsByClassName(names);
3.6 document.getElementsByName() 返回拥有相应name值的元素
document.getElementsByName
方法用于选择拥有name
属性的 HTML 元素(比如<form>
、<radio>
、<img>
、<frame>
、<embed>
和<object>
等),返回一个类似数组的的对象(NodeList
实例),因为name
属性相同的元素可能不止一个。
// 表单为 <form name="x"></form>
var forms = document.getElementsByName('x');
forms[0].tagName // "FORM"
3.7 document.getElementById() 返回匹配指定id
属性的元素节点
document.getElementById
方法返回匹配指定id
属性的元素节点。如果没有发现匹配的节点,则返回null
。
var elem = document.getElementById('para1');
注意,该方法的参数是大小写敏感的。比如,如果某个节点的id
属性是main
,那么document.getElementById('Main')
将返回null
。
**document.getElementById
方法与document.querySelector
方法都能获取元素节点,**不同之处是document.querySelector
方法的参数使用 CSS 选择器语法,document.getElementById
方法的参数是元素的id
属性。
document.getElementById('myElement')
document.querySelector('#myElement')
上面代码中,两个方法都能选中id
为myElement
的元素,但是document.getElementById()
比document.querySelector()
效率高得多。
另外,这个方法只能在document
对象上使用,不能在其他元素节点上使用。
3.8 document.elementFromPoint(x, y) 返回位于页面指定坐标最上层的元素节点,document.elementsFromPoint(x, y) 返回一个数组,成员是位于指定坐标的所有元素
document.elementFromPoint
方法返回位于页面指定位置最上层的元素节点。
var element = document.elementFromPoint(50, 50);
上面代码选中在(50, 50)
这个坐标位置的最上层的那个 HTML 元素。
elementFromPoint
方法的两个参数,依次是相对于当前视口左上角的横坐标和纵坐标,单位是像素。如果位于该位置的 HTML 元素不可返回(比如文本框的滚动条),则返回它的父元素(比如文本框)。如果坐标值无意义(比如负值或超过视口大小),则返回null
。
document.elementsFromPoint()
返回一个数组,成员是位于指定坐标(相对于视口)的所有元素。
var elements = document.elementsFromPoint(x, y);
注意:这两个方法的坐标都是相对于视口,就是说当滚动条滚动时,选中的元素可能会跟着变化。
3.9 document.createElement() 创建元素节点,并返回
document.createElement
方法用来生成元素节点,并返回该节点。
var newDiv = document.createElement('div');
createElement
方法的参数为元素的标签名,即元素节点的tagName
属性,对于 HTML 网页大小写不敏感,即参数为div
或DIV
返回的是同一种节点。如果参数里面包含尖括号(即<
和>
)会报错。
document.createElement('<div>');
// DOMException: The tag name provided ('<div>') is not a valid name
注意,document.createElement
的参数可以是自定义的标签名。
document.createElement('foo');
3.10 document.createTextNode() 创建文本节点,并返回
document.createTextNode
方法用来生成文本节点(Text
实例),并返回该节点。它的参数是文本节点的内容。
var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hello');
newDiv.appendChild(newContent);
上面代码新建一个div
节点和一个文本节点,然后将文本节点插入div
节点。
这个方法可以确保返回的节点,被浏览器当作文本渲染,而不是当作 HTML 代码渲染。因此,可以用来展示用户的输入,避免 XSS 攻击。
var div = document.createElement('div');
div.appendChild(document.createTextNode('<span>Foo & bar</span>'));
console.log(div.innerHTML)
// <span>Foo & bar</span>
上面代码中,createTextNode
方法对大于号和小于号进行转义,从而保证即使用户输入的内容包含恶意代码,也能正确显示。
需要注意的是,该方法不对单引号和双引号转义,所以不能用来对 HTML 属性赋值。
function escapeHtml(str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
};
var userWebsite = '" onmouseover="alert(\'derp\')" "';
var profileLink = '<a href="' + escapeHtml(userWebsite) + '">Bob</a>';
var div = document.getElementById('target');
div.innerHTML = profileLink;
// <a href="" onmouseover="alert('derp')" "">Bob</a>
上面代码中,由于createTextNode
方法不转义双引号,导致onmouseover
方法被注入了代码。
3.11 document.createAttribute() 创建属性节点,并返回
document.createAttribute
方法生成一个新的属性节点(Attr
实例),并返回它。
var attribute = document.createAttribute(name);
document.createAttribute
方法的参数name
,是属性的名称。
var node = document.getElementById('div1');
var a = document.createAttribute('my_attrib');
a.value = 'newVal';
node.setAttributeNode(a);
// 或者
node.setAttribute('my_attrib', 'newVal');
上面代码为div1
节点,插入一个值为newVal
的my_attrib
属性。
3.12 document.createComment() 创建注释节点,并返回
document.createComment
方法生成一个新的注释节点,并返回该节点。
var CommentNode = document.createComment(data);
document.createComment
方法的参数是一个字符串,会成为注释节点的内容。
3.13 document.createDocumentFragment() 创建空文档片段对象
document.createDocumentFragment
方法生成一个空的文档片段对象(DocumentFragment
实例)。
var docFragment = document.createDocumentFragment();
DocumentFragment
是一个存在于内存的 DOM 片段,不属于当前文档,常常用来生成一段较复杂的 DOM 结构,然后再插入当前文档。这样做的好处在于,因为DocumentFragment
不属于当前文档,对它的任何改动,都不会引发网页的重新渲染,比直接修改当前文档的 DOM 有更好的性能表现。
var docfrag = document.createDocumentFragment();
[1, 2, 3, 4].forEach(function (e) {
var li = document.createElement('li');
li.textContent = e;
docfrag.appendChild(li);
});
var element = document.getElementById('ul');
element.appendChild(docfrag);
上面代码中,文档片断docfrag
包含四个<li>
节点,这些子节点被一次性插入了当前文档。
3.14 document.createEvent() 创建事件对象(Event实例)
document.createEvent
方法生成一个事件对象(Event
实例),该对象可以被element.dispatchEvent
方法使用,触发指定事件。
var event = document.createEvent(type);
document.createEvent
方法的参数是事件类型,比如UIEvents
、MouseEvents
、MutationEvents
、HTMLEvents
。
var event = document.createEvent('Event');
event.initEvent('build', true, true);
document.addEventListener('build', function (e) {
console.log(e.type); // "build"
}, false);
document.dispatchEvent(event);
上面代码新建了一个名为build
的事件实例,然后触发该事件。
3.15 document.addEventListener() 添加事件监听,document.removeEventListener() 移除事件监听,document.dispatchEvent() 触发事件
这三个方法用于处理document
节点的事件。它们都继承自EventTarget
接口,详细介绍参见《EventTarget 接口》一章。
// 添加事件监听函数
document.addEventListener('click', listener, false);
// 移除事件监听函数
document.removeEventListener('click', listener, false);
// 触发事件
var event = new Event('click');
document.dispatchEvent(event);
3.16 document.hasFocus() 判断当前文档是否有元素被激活或获取焦点
document.hasFocus
方法返回一个布尔值,表示当前文档之中是否有元素被激活或获得焦点。
var focused = document.hasFocus();
注意,有焦点的文档必定被激活(active),反之不成立,激活的文档未必有焦点。比如,用户点击按钮,从当前窗口跳出一个新窗口,该新窗口就是激活的,但是不拥有焦点。
3.17 document.adoptNode() 采用外部节点,document.importNode() 输入(拷贝)外部节点
document.adoptNode
方法将某个节点及其子节点,从原来所在的文档或DocumentFragment
里面移除,归属当前document
对象,返回插入后的新节点。插入的节点对象的ownerDocument
属性,会变成当前的document
对象,而parentNode
属性是null
。
var node = document.adoptNode(externalNode); // externalNode 外部节点
document.appendChild(node);
注意,document.adoptNode
方法只是改变了节点的归属,并没有将这个节点插入新的文档树。所以,还要再用appendChild
方法或insertBefore
方法,将新节点插入当前文档树。
document.importNode
方法则是从原来所在的文档或DocumentFragment
里面,拷贝某个节点及其子节点,让它们归属当前document
对象。拷贝的节点对象的ownerDocument
属性,会变成当前的document
对象,而parentNode
属性是null
。
var node = document.importNode(externalNode, deep); // externalNode 外部节点 deep是否深拷贝
document.importNode
方法的第一个参数是外部节点,第二个参数是一个布尔值,表示对外部节点是深拷贝还是浅拷贝,默认是浅拷贝(false)。虽然第二个参数是可选的,但是建议总是保留这个参数,并设为true
。
注意,document.importNode
方法只是拷贝外部节点,这时该节点的父节点是null
。下一步还必须将这个节点插入当前文档树。
var iframe = document.getElementsByTagName('iframe')[0];
var oldNode = iframe.contentWindow.document.getElementById('myNode');
var newNode = document.importNode(oldNode, true);
document.getElementById("container").appendChild(newNode);
上面代码从iframe
窗口,拷贝一个指定节点myNode
,插入当前文档。
3.18 document.createNodeIterator() 返回一个子节点遍历器(NodeFilter实例)
document.createNodeIterator
方法返回一个子节点遍历器(NodeFilter实例)。
var nodeIterator = document.createNodeIterator(
document.body, // 所要遍历的根节点
NodeFilter.SHOW_ELEMENT // 所要遍历的节点类型
);
上面代码返回<body>
元素子节点的遍历器。
document.createNodeIterator
方法第一个参数为所要遍历的根节点,第二个参数为所要遍历的节点类型,这里指定为元素节点(NodeFilter.SHOW_ELEMENT
)。几种主要的节点类型写法如下。
- 所有节点:NodeFilter.SHOW_ALL
- 元素节点:NodeFilter.SHOW_ELEMENT
- 文本节点:NodeFilter.SHOW_TEXT
- 评论节点:NodeFilter.SHOW_COMMENT
document.createNodeIterator
方法返回一个“遍历器”对象(NodeFilter
实例)。该实例的nextNode()
方法和previousNode()
方法,可以用来遍历所有子节点。
每次调用NodeFilter实例的nextNode() / previousNode()方法都会把当前指针往 下一个 / 上一个节点移动。
var nodeIterator = document.createNodeIterator(document.body);
var pars = [];
var currentNode;
while (currentNode = nodeIterator.nextNode()) {
// js中所有运算符都有返回值,为运算结果。这里面即给currentNode赋值了,还会自动调用Boolean()进行布尔运算。
// 每次调用NodeFilter实例的nextNode()方法都会返回下一个节点
pars.push(currentNode);
}
上面代码中,使用遍历器的nextNode
方法,将根节点的所有子节点,依次读入一个数组。nextNode
方法先返回遍历器的内部指针所在的节点,然后会将指针移向下一个节点。所有成员遍历完成后,返回null
。previousNode
方法则是先将指针移向上一个节点,然后返回该节点。
var nodeIterator = document.createNodeIterator(
document.body,
NodeFilter.SHOW_ELEMENT
);
var currentNode = nodeIterator.nextNode();
var previousNode = nodeIterator.previousNode();
currentNode === previousNode // true
上面代码中,currentNode
和previousNode
都指向同一个的节点。
注意,遍历器返回的第一个节点,总是根节点。
pars[0] === document.body // true
- 点赞
- 收藏
- 关注作者
评论(0)