Document 概述
Document 它又是什么
Document 节点
概述
document
节点对象代表整个文档,每张网页都有自己的document
对象。window.document
属性就指向这个对象。只要浏览器开始载入 HTML 文档,该对象就存在了,可以直接使用。
document
对象有不同的办法可以获取。
- 正常的网页,直接使用
document
或window.document
。 iframe
框架里面的网页,使用iframe
节点的contentDocument
属性。- Ajax 操作返回的文档,使用
XMLHttpRequest
对象的responseXML
属性。 - 内部节点的
ownerDocument
属性。
document
对象继承了EventTarget
接口和Node
接口,并且混入(mixin)了ParentNode
接口。这意味着,这些接口的方法都可以在document
对象上调用。除此之外,document
对象还有很多自己的属性和方法。
属性
快捷方式属性
以下属性是指向文档内部的某个节点的快捷方式。
(1)document.defaultView
document.defaultView
属性返回document
对象所属的window
对象。如果当前文档不属于window
对象,该属性返回null
。
(2)document.doctype
对于 HTML 文档来说,document
对象一般有两个子节点。第一个子节点是document.doctype
,指向<DOCTYPE>
节点,即文档类型(Document Type Declaration,简写DTD)节点。HTML 的文档类型节点,一般写成<!DOCTYPE html>
。如果网页没有声明 DTD,该属性返回null
。
document.firstChild
通常就返回这个节点。
(3)document.documentElement
document.documentElement
属性返回当前文档的根元素节点(root)。它通常是document
节点的第二个子节点,紧跟在document.doctype
节点后面。HTML网页的该属性,一般是<html>
节点。
(4)document.body,document.head
document.body
属性指向<body>
节点,document.head
属性指向<head>
节点。
这两个属性总是存在的,如果网页源码里面省略了<head>
或<body>
,浏览器会自动创建。另外,这两个属性是可写的,如果改写它们的值,相当于移除所有子节点。
(5)document.scrollingElement
document.scrollingElement
属性返回文档的滚动元素。也就是说,当文档整体滚动时,到底是哪个元素在滚动。
标准模式下,这个属性返回的文档的根元素document.documentElement
(即<html>
)。兼容(quirk)模式下,返回的是<body>
元素,如果该元素不存在,返回null
。
(6)document.activeElement
document.activeElement
属性返回获得当前焦点(focus)的 DOM 元素。通常,这个属性返回的是<input>
、<textarea>
、<select>
等表单元素,如果当前没有焦点元素,返回<body>
元素或null
。
(7)document.fullscreenElement
document.fullscreenElement
属性返回当前以全屏状态展示的 DOM 元素。如果不是全屏状态,该属性返回null
。
上面代码中,通过document.fullscreenElement
可以知道<video>
元素有没有处在全屏状态,从而判断用户行为。
节点集合属性
以下属性返回一个HTMLCollection
实例,表示文档内部特定元素的集合。这些集合都是动态的,原节点有任何变化,立刻会反映在集合中。
(1)document.links
document.links
属性返回当前文档所有设定了href
属性的<a>
及<area>
节点。
(2)document.forms
document.forms
属性返回所有<form>
表单节点。
上面代码获取文档第一个表单。
除了使用位置序号,id
属性和name
属性也可以用来引用表单。
(3)document.images
document.images
属性返回页面所有<img>
图片节点。
上面代码在所有img
标签中,寻找某张图片。
(4)document.embeds,document.plugins
document.embeds
属性和document.plugins
属性,都返回所有<embed>
节点。
(5)document.scripts
document.scripts
属性返回所有<script>
节点。
(6)document.styleSheets
document.styleSheets
属性返回网页内嵌或引入的 CSS 样式表集合,详细介绍请看《CSS 操作》一章。
(7)小结
除了document.styleSheets
属性,以上的其他集合属性返回的都是HTMLCollection
实例。document.styleSheets
属性返回的是StyleSheetList
实例。
HTMLCollection
实例是类似数组的对象,所以上面这些属性都有length
属性,都可以使用方括号运算符引用成员。如果成员有id
或name
属性,还可以用这两个属性的值,在HTMLCollection
实例上引用到这个成员。
文档静态信息属性
以下属性返回文档信息。
(1)document.documentURI,document.URL
document.documentURI
属性和document.URL
属性都返回一个字符串,表示当前文档的网址。不同之处是它们继承自不同的接口,documentURI
继承自Document
接口,可用于所有文档;URL
继承自HTMLDocument
接口,只能用于 HTML 文档。
如果文档的锚点(#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
Location
对象是浏览器提供的原生对象,提供 URL 相关的信息和操作方法。通过window.location
和document.location
属性,可以拿到这个对象。
关于这个对象的详细介绍,请看《浏览器模型》部分的《Location 对象》章节。
(4)document.lastModified
document.lastModified
属性返回一个字符串,表示当前文档最后修改的时间。不同浏览器的返回值,日期格式是不一样的。
注意,document.lastModified
属性的值是字符串,所以不能直接用来比较。Date.parse
方法将其转为Date
实例,才能比较两个网页。
如果页面上有 JavaScript 生成的内容,document.lastModified
属性返回的总是当前时间。
(5)document.title
document.title
属性返回当前文档的标题。默认情况下,返回<title>
节点的值。但是该属性是可写的,一旦被修改,就返回修改后的值。
(6)document.characterSet
document.characterSet
属性返回当前文档的编码,比如UTF-8
、ISO-8859-1
等等。
(7)document.referrer
document.referrer
属性返回一个字符串,表示当前文档的访问者来自哪里。
如果无法获取来源,或者用户直接键入网址而不是从其他网页点击进入,document.referrer
返回一个空字符串。
document.referrer
的值,总是与 HTTP 头信息的Referer
字段保持一致。但是,document.referrer
的拼写有两个r
,而头信息的Referer
字段只有一个r
。
(8)document.dir
document.dir
返回一个字符串,表示文字方向。它只有两个可能的值:rtl
表示文字从右到左,阿拉伯文是这种方式;ltr
表示文字从左到右,包括英语和汉语在内的大多数文字采用这种方式。
(9)document.compatMode
compatMode
属性返回浏览器处理文档的模式,可能的值为BackCompat
(向后兼容模式)和CSS1Compat
(严格模式)。
一般来说,如果网页代码的第一行设置了明确的DOCTYPE
(比如<!doctype html>
),document.compatMode
的值都为CSS1Compat
。
文档状态属性
(1)document.hidden
document.hidden
属性返回一个布尔值,表示当前页面是否可见。如果窗口最小化、浏览器切换了 Tab,都会导致导致页面不可见,使得document.hidden
返回true
。
这个属性是 Page Visibility API 引入的,一般都是配合这个 API 使用。
(2)document.visibilityState
document.visibilityState
返回文档的可见状态。
它的值有四种可能。
visible
:页面可见。注意,页面可能是部分可见,即不是焦点窗口,前面被其他窗口部分挡住了。hidden
:页面不可见,有可能窗口最小化,或者浏览器切换到了另一个 Tab。prerender
:页面处于正在渲染状态,对于用户来说,该页面不可见。unloaded
:页面从内存里面卸载了。
这个属性可以用在页面加载时,防止加载某些资源;或者页面不可见时,停掉一些页面功能。
(3)document.readyState
document.readyState
属性返回当前文档的状态,共有三种可能的值。
loading
:加载 HTML 代码阶段(尚未完成解析)interactive
:加载外部资源阶段complete
:加载完成
这个属性变化的过程如下。
- 浏览器开始解析 HTML 文档,
document.readyState
属性等于loading
。 - 浏览器遇到 HTML 文档中的
<script>
元素,并且没有async
或defer
属性,就暂停解析,开始执行脚本,这时document.readyState
属性还是等于loading
。 - HTML 文档解析完成,
document.readyState
属性变成interactive
。 - 浏览器等待图片、样式表、字体文件等外部资源加载完成,一旦全部加载完成,
document.readyState
属性变成complete
。
下面的代码用来检查网页是否加载成功。
另外,每次状态变化都会触发一个readystatechange
事件。
document.cookie
document.cookie
属性用来操作浏览器 Cookie,详见《浏览器模型》部分的《Cookie》章节。
document.designMode
document.designMode
属性控制当前文档是否可编辑。该属性只有两个值on
和off
,默认值为off
。一旦设为on
,用户就可以编辑整个文档的内容。
下面代码打开iframe
元素内部文档的designMode
属性,就能将其变为一个所见即所得的编辑器。
document.currentScript
document.currentScript
属性只用在<script>
元素的内嵌脚本或加载的外部脚本之中,返回当前脚本所在的那个 DOM 节点,即<script>
元素的 DOM 节点。
上面代码中,document.currentScript
就是<script>
元素节点。
document.implementation
document.implementation
属性返回一个DOMImplementation
对象。该对象有三个方法,主要用于创建独立于当前文档的新的 Document 对象。
DOMImplementation.createDocument()
:创建一个 XML 文档。DOMImplementation.createHTMLDocument()
:创建一个 HTML 文档。DOMImplementation.createDocumentType()
:创建一个 DocumentType 对象。
下面是创建 HTML 文档的例子。
上面代码中,第一步生成一个新的 HTML 文档doc
,然后用它的根元素doc.documentElement
替换掉document.documentElement
。这会使得当前文档的内容全部消失,变成hello world
。
方法
document.open(),document.close()
document.open
方法清除当前文档所有内容,使得文档处于可写状态,供document.write
方法写入内容。
document.close
方法用来关闭document.open()
打开的文档。
document.write(),document.writeln()
document.write
方法用于向当前文档写入内容。
在网页的首次渲染阶段,只要页面没有关闭写入(即没有执行document.close()
),document.write
写入的内容就会追加在已有内容的后面。
注意,document.write
会当作 HTML 代码解析,不会转义。
上面代码中,document.write
会将<p>
当作 HTML 标签解释。
如果页面已经解析完成(DOMContentLoaded
事件发生之后),再调用write
方法,它会先调用open
方法,擦除当前文档所有内容,然后再写入。
如果在页面渲染过程中调用write
方法,并不会自动调用open
方法。(可以理解成,open
方法已调用,但close
方法还未调用。)
在浏览器打开上面网页,将会显示hello world
。
document.write
是 JavaScript 语言标准化之前就存在的方法,现在完全有更符合标准的方法向文档写入内容(比如对innerHTML
属性赋值)。所以,除了某些特殊情况,应该尽量避免使用document.write
这个方法。
document.writeln
方法与write
方法完全一致,除了会在输出内容的尾部添加换行符。
注意,writeln
方法添加的是 ASCII 码的换行符,渲染成 HTML 网页时不起作用,即在网页上显示不出换行。网页上的换行,必须显式写入<br>
。
document.querySelector(),document.querySelectorAll()
document.querySelector
方法接受一个 CSS 选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null
。
document.querySelectorAll
方法与querySelector
用法类似,区别是返回一个NodeList
对象,包含所有匹配给定选择器的节点。
这两个方法的参数,可以是逗号分隔的多个 CSS 选择器,返回匹配其中一个选择器的元素节点,这与 CSS 选择器的规则是一致的。
上面代码返回class
属性是note
或alert
的div
元素。
这两个方法都支持复杂的 CSS 选择器。
但是,它们不支持 CSS 伪元素的选择器(比如:first-line
和:first-letter
)和伪类的选择器(比如:link
和:visited
),即无法选中伪元素和伪类。
如果querySelectorAll
方法的参数是字符串*
,则会返回文档中的所有元素节点。另外,querySelectorAll
的返回结果不是动态集合,不会实时反映元素节点的变化。
最后,这两个方法除了定义在document
对象上,还定义在元素节点上,即在元素节点上也可以调用。
document.getElementsByTagName()
document.getElementsByTagName()
方法搜索 HTML 标签名,返回符合条件的元素。它的返回值是一个类似数组对象(HTMLCollection
实例),可以实时反映 HTML 文档的变化。如果没有任何匹配的元素,就返回一个空集。
上面代码返回当前文档的所有p
元素节点。
HTML 标签名是大小写不敏感的,因此getElementsByTagName()
方法的参数也是大小写不敏感的。另外,返回结果中,各个成员的顺序就是它们在文档中出现的顺序。
如果传入*
,就可以返回文档中所有 HTML 元素。
注意,元素节点本身也定义了getElementsByTagName
方法,返回该元素的后代元素中符合条件的元素。也就是说,这个方法不仅可以在document
对象上调用,也可以在任何元素节点上调用。
上面代码选中第一个p
元素内部的所有span
元素。
document.getElementsByClassName()
document.getElementsByClassName()
方法返回一个类似数组的对象(HTMLCollection
实例),包括了所有class
名字符合指定条件的元素,元素的变化实时反映在返回结果中。
由于class
是保留字,所以 JavaScript 一律使用className
表示 CSS 的class
。
参数可以是多个class
,它们之间使用空格分隔。
上面代码返回同时具有foo
和bar
两个class
的元素,foo
和bar
的顺序不重要。
注意,正常模式下,CSS 的class
是大小写敏感的。(quirks mode
下,大小写不敏感。)
与getElementsByTagName()
方法一样,getElementsByClassName()
方法不仅可以在document
对象上调用,也可以在任何元素节点上调用。
document.getElementsByName()
document.getElementsByName()
方法用于选择拥有name
属性的 HTML 元素(比如<form>
、<radio>
、<img>
、<frame>
、<embed>
和<object>
等),返回一个类似数组的的对象(NodeList
实例),因为name
属性相同的元素可能不止一个。
document.getElementById()
document.getElementById()
方法返回匹配指定id
属性的元素节点。如果没有发现匹配的节点,则返回null
。
注意,该方法的参数是大小写敏感的。比如,如果某个节点的id
属性是main
,那么document.getElementById('Main')
将返回null
。
document.getElementById()
方法与document.querySelector()
方法都能获取元素节点,不同之处是document.querySelector()
方法的参数使用 CSS 选择器语法,document.getElementById()
方法的参数是元素的id
属性。
上面代码中,两个方法都能选中id
为myElement
的元素,但是document.getElementById()
比document.querySelector()
效率高得多。
另外,这个方法只能在document
对象上使用,不能在其他元素节点上使用。
document.elementFromPoint(),document.elementsFromPoint()
document.elementFromPoint()
方法返回位于页面指定位置最上层的元素节点。
上面代码选中在(50, 50)
这个坐标位置的最上层的那个 HTML 元素。
elementFromPoint
方法的两个参数,依次是相对于当前视口左上角的横坐标和纵坐标,单位是像素。如果位于该位置的 HTML 元素不可返回(比如文本框的滚动条),则返回它的父元素(比如文本框)。如果坐标值无意义(比如负值或超过视口大小),则返回null
。
document.elementsFromPoint()
返回一个数组,成员是位于指定坐标(相对于视口)的所有元素。
document.createElement()
document.createElement
方法用来生成元素节点,并返回该节点。
createElement
方法的参数为元素的标签名,即元素节点的tagName
属性,对于 HTML 网页大小写不敏感,即参数为div
或DIV
返回的是同一种节点。如果参数里面包含尖括号(即<
和>
)会报错。
注意,document.createElement
的参数可以是自定义的标签名。
document.createTextNode()
document.createTextNode
方法用来生成文本节点(Text
实例),并返回该节点。它的参数是文本节点的内容。
上面代码新建一个div
节点和一个文本节点,然后将文本节点插入div
节点。
这个方法可以确保返回的节点,被浏览器当作文本渲染,而不是当作 HTML 代码渲染。因此,可以用来展示用户的输入,避免 XSS 攻击。
上面代码中,createTextNode
方法对大于号和小于号进行转义,从而保证即使用户输入的内容包含恶意代码,也能正确显示。
需要注意的是,该方法不对单引号和双引号转义,所以不能用来对 HTML 属性赋值。
上面代码中,由于createTextNode
方法不转义双引号,导致onmouseover
方法被注入了代码。
document.createAttribute()
document.createAttribute
方法生成一个新的属性节点(Attr
实例),并返回它。
document.createAttribute
方法的参数name
,是属性的名称。
上面代码为div1
节点,插入一个值为newVal
的my_attrib
属性。
document.createComment()
document.createComment
方法生成一个新的注释节点,并返回该节点。
document.createComment
方法的参数是一个字符串,会成为注释节点的内容。
document.createDocumentFragment()
document.createDocumentFragment
方法生成一个空的文档片段对象(DocumentFragment
实例)。
DocumentFragment
是一个存在于内存的 DOM 片段,不属于当前文档,常常用来生成一段较复杂的 DOM 结构,然后再插入当前文档。这样做的好处在于,因为DocumentFragment
不属于当前文档,对它的任何改动,都不会引发网页的重新渲染,比直接修改当前文档的 DOM 有更好的性能表现。
上面代码中,文档片段docfrag
包含四个<li>
节点,这些子节点被一次性插入了当前文档。
document.createEvent()
document.createEvent
方法生成一个事件对象(Event
实例),该对象可以被element.dispatchEvent
方法使用,触发指定事件。
document.createEvent
方法的参数是事件类型,比如UIEvents
、MouseEvents
、MutationEvents
、HTMLEvents
。
上面代码新建了一个名为build
的事件实例,然后触发该事件。
document.addEventListener(),document.removeEventListener(),document.dispatchEvent()
这三个方法用于处理document
节点的事件。它们都继承自EventTarget
接口,详细介绍参见《EventTarget 接口》一章。
document.hasFocus()
document.hasFocus
方法返回一个布尔值,表示当前文档之中是否有元素被激活或获得焦点。
注意,有焦点的文档必定被激活(active),反之不成立,激活的文档未必有焦点。比如,用户点击按钮,从当前窗口跳出一个新窗口,该新窗口就是激活的,但是不拥有焦点。
document.adoptNode(),document.importNode()
document.adoptNode
方法将某个节点及其子节点,从原来所在的文档或DocumentFragment
里面移除,归属当前document
对象,返回插入后的新节点。插入的节点对象的ownerDocument
属性,会变成当前的document
对象,而parentNode
属性是null
。
注意,document.adoptNode
方法只是改变了节点的归属,并没有将这个节点插入新的文档树。所以,还要再用appendChild
方法或insertBefore
方法,将新节点插入当前文档树。
document.importNode
方法则是从原来所在的文档或DocumentFragment
里面,拷贝某个节点及其子节点,让它们归属当前document
对象。拷贝的节点对象的ownerDocument
属性,会变成当前的document
对象,而parentNode
属性是null
。
document.importNode
方法的第一个参数是外部节点,第二个参数是一个布尔值,表示对外部节点是深拷贝还是浅拷贝,默认是浅拷贝(false)。虽然第二个参数是可选的,但是建议总是保留这个参数,并设为true
。
注意,document.importNode
方法只是拷贝外部节点,这时该节点的父节点是null
。下一步还必须将这个节点插入当前文档树。
上面代码从iframe
窗口,拷贝一个指定节点myNode
,插入当前文档。
document.createNodeIterator()
document.createNodeIterator
方法返回一个子节点遍历器。
上面代码返回<body>
元素子节点的遍历器。
document.createNodeIterator
方法第一个参数为所要遍历的根节点,第二个参数为所要遍历的节点类型,这里指定为元素节点(NodeFilter.SHOW_ELEMENT
)。几种主要的节点类型写法如下。
- 所有节点:NodeFilter.SHOW_ALL
- 元素节点:NodeFilter.SHOW_ELEMENT
- 文本节点:NodeFilter.SHOW_TEXT
- 评论节点:NodeFilter.SHOW_COMMENT
document.createNodeIterator
方法返回一个“遍历器”对象(NodeFilter
实例)。该实例的nextNode()
方法和previousNode()
方法,可以用来遍历所有子节点。
上面代码中,使用遍历器的nextNode
方法,将根节点的所有子节点,依次读入一个数组。nextNode
方法先返回遍历器的内部指针所在的节点,然后会将指针移向下一个节点。所有成员遍历完成后,返回null
。previousNode
方法则是先将指针移向上一个节点,然后返回该节点。
上面代码中,currentNode
和previousNode
都指向同一个的节点。
注意,遍历器返回的第一个节点,总是根节点。
document.createTreeWalker()
document.createTreeWalker
方法返回一个 DOM 的子树遍历器。它与document.createNodeIterator
方法基本是类似的,区别在于它返回的是TreeWalker
实例,后者返回的是NodeIterator
实例。另外,它的第一个节点不是根节点。
document.createTreeWalker
方法的第一个参数是所要遍历的根节点,第二个参数指定所要遍历的节点类型(与document.createNodeIterator
方法的第二个参数相同)。
上面代码遍历<body>
节点下属的所有元素节点,将它们插入nodeList
数组。
document.execCommand(),document.queryCommandSupported(),document.queryCommandEnabled()
(1)document.execCommand()
如果document.designMode
属性设为on
,那么整个文档用户可编辑;如果元素的contenteditable
属性设为true
,那么该元素可编辑。这两种情况下,可以使用document.execCommand()
方法,改变内容的样式,比如document.execCommand('bold')
会使得字体加粗。
该方法接受三个参数。
command
:字符串,表示所要实施的样式。showDefaultUI
:布尔值,表示是否要使用默认的用户界面,建议总是设为false
。input
:字符串,表示该样式的辅助内容,比如生成超级链接时,这个参数就是所要链接的网址。如果第二个参数设为true
,那么浏览器会弹出提示框,要求用户在提示框输入该参数。但是,不是所有浏览器都支持这样做,为了兼容性,还是需要自己部署获取这个参数的方式。
上面代码中,先提示用户输入所要链接的网址,然后手动生成超级链接。注意,第二个参数是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。这些值都可以用作第一个参数,它们的含义不难从字面上看出来。
(2)document.queryCommandSupported()
document.queryCommandSupported()
方法返回一个布尔值,表示浏览器是否支持document.execCommand()
的某个命令。
(3)document.queryCommandEnabled()
document.queryCommandEnabled()
方法返回一个布尔值,表示当前是否可用document.execCommand()
的某个命令。比如,bold
(加粗)命令只有存在文本选中时才可用,如果没有选中文本,就不可用。
上面代码中,先判断浏览器是否支持copy
命令(允许可编辑区域的选中内容,复制到剪贴板),如果支持,就新建一个临时文本框,里面写入内容“你好”,并将其选中。然后,判断是否选中成功,如果成功,就将“你好”复制到剪贴板,再删除那个临时文本框。
document.getSelection()
这个方法指向window.getSelection()
,参见window
对象一节的介绍。