JavaScript-DOM(二)
五、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
3.19 document.createTreeWalker() 返回一个DOM的子树遍历器(TreeWalker实例)
document.createTreeWalker
方法返回一个 DOM 的子树遍历器。它与document.createNodeIterator
方法基本是类似的,区别在于它返回的是TreeWalker
实例,后者返回的是NodeIterator
实例。另外,它的第一个节点不是根节点。
document.createTreeWalker
方法的第一个参数是所要遍历的根节点,第二个参数指定所要遍历的节点类型(与document.createNodeIterator
方法的第二个参数相同)。
和document.createNodeIterator
方法区别二是:TreeWalker
实例有currentNode
属性。
var treeWalker = document.createTreeWalker(
document.body, // 所要遍历的根节点
NodeFilter.SHOW_ELEMENT // 所要遍历的节点类型
);
var nodeList = [];
// 每调用一次nextNode(),currentNode属性的值将改为下一个节点
while(treeWalker.nextNode()) {
nodeList.push(treeWalker.currentNode); // TreeWalker实例有currentNode属性
}
上面代码遍历<body>
节点下属的所有元素节点,将它们插入nodeList
数组。
3.20 document.execCommand(),document.queryCommandSupported(),document.queryCommandEnabled()
(1)document.execCommand() 执行命令(实现复制文本等功能,富文本编辑器大量使用此方法)
概念一: 当一个HTML文档切换到设计模式时,document
暴露 execCommand
方法,该方法允许运行命令来操纵可编辑内容区域的元素。
概念二: execCommand方法是执行一个对当前文档,当前选择或者给出范围的命令。处理Html数据时常用
概念三:如果document.designMode
属性设为on
,那么整个文档用户可编辑;如果元素的contenteditable
属性设为true
,那么该元素可编辑。这两种情况下,可以使用document.execCommand()
方法,改变内容的样式,比如document.execCommand('bold')
会使得字体加粗。
document.execCommand(command, showDefaultUI, input)
该方法接受三个参数。
command
:字符串,表示所要实施的样式。showDefaultUI
:布尔值,表示是否要使用默认的用户界面,建议总是设为false
。input
:字符串,表示该样式的辅助内容,比如生成超级链接时,这个参数就是所要链接的网址。如果第二个参数设为true
,那么浏览器会弹出提示框,要求用户在提示框输入该参数。但是,不是所有浏览器都支持这样做,为了兼容性,还是需要自己部署获取这个参数的方式。
var url = window.prompt('请输入网址');
if (url) {
document.execCommand('createlink', false, url);
}
上面代码中,先提示用户输入所要链接的网址,然后手动生成超级链接。注意,第二个参数是false
,表示此时不需要自动弹出提示框。
document.execCommand()
的返回值是一个布尔值。如果为false
,表示这个方法无法生效。
这个方法大部分情况下,只对选中的内容生效。如果有多个内容可编辑区域,那么只对当前焦点所在的元素生效。
document.execCommand()
方法可以执行的样式改变有很多种,下面是其中的一些:bold、insertLineBreak、selectAll、createLink、insertOrderedList、subscript、delete、insertUnorderedList、superscript、formatBlock、insertParagraph、undo、forwardDelete、insertText、unlink、insertImage、italic、unselect、insertHTML、redo。这些值都可以用作第一个参数,它们的含义不难从字面上看出来。
命令列表
1. 2D-Position 允许通过拖曳移动绝对定位的对象。
2. AbsolutePosition 设定元素的 position 属性为“absolute”(绝对)。
3. BackColor 设置或获取当前选中区的背景颜色。
4. BlockDirLTR 目前尚未支持。
5. BlockDirRTL 目前尚未支持。
6. Bold 切换当前选中区的粗体显示与否。
7. BrowseMode 目前尚未支持。
8. Copy 将当前选中区复制到剪贴板。
9. CreateBookmark 创建一个书签锚或获取当前选中区或插入点的书签锚的名称。
10.CreateLink 在当前选中区上插入超级链接,或显示一个对话框允许用户指定要为当前选中区插入的超级链接的 URL。
11.Cut 将当前选中区复制到剪贴板并删除之。
12.Delete 删除当前选中区。
13.DirLTR 目前尚未支持。
14.DirRTL 目前尚未支持。
15.EditMode 目前尚未支持。
16.FontName 设置或获取当前选中区的字体。
17.FontSize 设置或获取当前选中区的字体大小。
18.ForeColor 设置或获取当前选中区的前景(文本)颜色。
19.FormatBlock 设置当前块格式化标签。
20.Indent 增加选中文本的缩进。
21.InlineDirLTR 目前尚未支持。
22.InlineDirRTL 目前尚未支持。
23.InsertButton 用按钮控件覆盖当前选中区。
24.InsertFieldset 用方框覆盖当前选中区。
25.InsertHorizontalRule 用水平线覆盖当前选中区。
26.InsertIFrame 用内嵌框架覆盖当前选中区。
27.InsertImage 用图像覆盖当前选中区。
28.InsertInputButton 用按钮控件覆盖当前选中区。
29.InsertInputCheckbox 用复选框控件覆盖当前选中区。
30.InsertInputFileUpload 用文件上载控件覆盖当前选中区。
31.InsertInputHidden 插入隐藏控件覆盖当前选中区。
32.InsertInputImage 用图像控件覆盖当前选中区。
33.InsertInputPassword 用密码控件覆盖当前选中区。
34.InsertInputRadio 用单选钮控件覆盖当前选中区。
35.InsertInputReset 用重置控件覆盖当前选中区。
36.InsertInputSubmit 用提交控件覆盖当前选中区。
37.InsertInputText 用文本控件覆盖当前选中区。
38.InsertMarquee 用空字幕覆盖当前选中区。
39.InsertOrderedList 切换当前选中区是编号列表还是常规格式化块。
40.InsertParagraph 用换行覆盖当前选中区。
41.InsertSelectDropdown 用下拉框控件覆盖当前选中区。
42.InsertSelectListbox 用列表框控件覆盖当前选中区。
43.InsertTextArea 用多行文本输入控件覆盖当前选中区。
44.InsertUnorderedList 切换当前选中区是项目符号列表还是常规格式化块。
45.Italic 切换当前选中区斜体显示与否。
46.JustifyCenter 将当前选中区在所在格式化块置中。
47.JustifyFull 目前尚未支持。
48.JustifyLeft 将当前选中区所在格式化块左对齐。
49.JustifyNone 目前尚未支持。
50.JustifyRight 将当前选中区所在格式化块右对齐。
51.LiveResize 迫使 MSHTML 编辑器在缩放或移动过程中持续更新元素外观,而不是只在移动或缩放完成后更新。
52.MultipleSelection 允许当用户按住 Shift 或 Ctrl 键时一次选中多于一个站点可选元素。
53.Open 打开。
54.Outdent 减少选中区所在格式化块的缩进。
55.OverWrite 切换文本状态的插入和覆盖。
56.Paste 用剪贴板内容覆盖当前选中区。
57.PlayImage 目前尚未支持。
58.Print 打开打印对话框以便用户可以打印当前页。
59.Redo 重做。
60.Refresh 刷新当前文档。
61.RemoveFormat 从当前选中区中删除格式化标签。
62.RemoveParaFormat 目前尚未支持。
63.SaveAs 将当前 Web 页面保存为文件。
64.SelectAll 选中整个文档。
65.SizeToControl 目前尚未支持。
66.SizeToControlHeight 目前尚未支持。
67.SizeToControlWidth 目前尚未支持。
68.Stop 停止。
69.StopImage 目前尚未支持。
70.StrikeThrough 目前尚未支持。
71.Subscript 目前尚未支持。
72.Superscript 目前尚未支持。
73.UnBookmark 从当前选中区中删除全部书签。
74.Underline 切换当前选中区的下划线显示与否。
75.Undo 撤消。
76.Unlink 从当前选中区中删除全部超级链接。
77.Unselect 清除当前选中区的选中状态。
(2)document.queryCommandSupported() 浏览器是否支持execCommand的某个命令
document.queryCommandSupported()
方法返回一个布尔值,表示浏览器是否支持document.execCommand()
的某个命令。
if (document.queryCommandSupported('SelectAll')) {
console.log('浏览器支持选中可编辑区域的所有内容');
}
(3)document.queryCommandEnabled() 当前是否可用execCommand的某个命令
document.queryCommandEnabled()
方法返回一个布尔值,表示当前是否可用document.execCommand()
的某个命令。比如,bold
(加粗)命令只有存在文本选中时才可用,如果没有选中文本,就不可用。
// HTML 代码为
// <input type="button" value="Copy" onclick="doCopy()">
function doCopy(){
// 浏览器是否支持 copy 命令(选中内容复制到剪贴板)
if (document.queryCommandSupported('copy')) {
copyText('你好');
}else{
console.log('浏览器不支持');
}
}
function copyText(text) {
var input = document.createElement('textarea');
document.body.appendChild(input);
input.value = text;
input.focus();
input.select();
// 当前是否有选中文字
if (document.queryCommandEnabled('copy')) {
var success = document.execCommand('copy');
input.remove();
console.log('Copy Ok');
} else {
console.log('queryCommandEnabled is false');
}
}
上面代码中,先判断浏览器是否支持copy
命令(允许可编辑区域的选中内容,复制到剪贴板),如果支持,就新建一个临时文本框,里面写入内容“你好”,并将其选中。然后,判断是否选中成功,如果成功,就将“你好”复制到剪贴板,再删除那个临时文本框。
3.21 document.getSelection()
这个方法指向window.getSelection()
,参见window
对象一节的介绍。
六、Element节点
Element
节点对象对应网页的 HTML 元素。每一个 HTML 元素,在 DOM 树上都会转化成一个Element
节点对象(以下简称元素节点)。
元素节点的nodeType
属性都是1
。
var p = document.querySelector('p');
p.nodeName // "P"
p.nodeType // 1 节点类型 1代表元素节点
Element
对象继承了Node
接口,因此**Node
的属性和方法在Element
对象都存在**。此外,不同的 HTML 元素对应的元素节点是不一样的,浏览器使用不同的构造函数,生成不同的元素节点,比如<a>
元素的节点对象由HTMLAnchorElement
构造函数生成,<button>
元素的节点对象由HTMLButtonElement
构造函数生成。因此,元素节点不是一种对象,而是一组对象,这些对象除了继承Element
的属性和方法,还有各自构造函数的属性和方法。
元素节点拥有 各自构造函数 的属性和方法,继承Element
的属性和方法,同时继承Node
的属性和方法。
- 点赞
- 收藏
- 关注作者
评论(0)