JavaScript-DOM(二)

举报
Johan Tian 发表于 2022/07/13 13:45:04 2022/07/13
【摘要】 五、Document节点 1、概述document节点对象代表整个文档,每张网页都有自己的document对象。window.document属性就指向这个对象。只要浏览器开始载入 HTML 文档,该对象就存在了,可以直接使用。document对象有不同的办法可以获取。正常的网页,直接使用document或window.document。iframe框架里面的网页,使用iframe节点的c...

五、Document节点

1、概述

document节点对象代表整个文档,每张网页都有自己的document对象。window.document属性就指向这个对象。只要浏览器开始载入 HTML 文档,该对象就存在了,可以直接使用。

document对象有不同的办法可以获取。

  • 正常的网页,直接使用documentwindow.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属性,都可以使用方括号运算符引用成员。如果成员有idname属性,还可以用这两个属性的值,在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.locationdocument.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-8ISO-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:加载完成
    这个属性变化的过程如下。
  1. 浏览器开始解析 HTML 文档,document.readyState属性等于loading
  2. 浏览器遇到 HTML 文档中的<script>元素,并且没有asyncdefer属性,就暂停解析,开始执行脚本,这时document.readyState属性还是等于loading
  3. HTML 文档解析完成,document.readyState属性变成interactive
  4. 浏览器等待图片、样式表、字体文件等外部资源加载完成,一旦全部加载完成,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属性控制当前文档是否可编辑。该属性只有两个值onoff,默认值为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属性是notealertdiv元素。

这两个方法都支持复杂的 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');

上面代码返回同时具有foobar两个class的元素foobar的顺序不重要。

注意,正常模式下,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')

上面代码中,两个方法都能选中idmyElement的元素,但是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 网页大小写不敏感,即参数为divDIV返回的是同一种节点。如果参数里面包含尖括号(即<>)会报错。

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)
// &lt;span&gt;Foo &amp; bar&lt;/span&gt;

上面代码中,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节点,插入一个值为newValmy_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方法的参数是事件类型,比如UIEventsMouseEventsMutationEventsHTMLEvents

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方法先返回遍历器的内部指针所在的节点,然后会将指针移向下一个节点。所有成员遍历完成后,返回nullpreviousNode方法则是先将指针移向上一个节点,然后返回该节点。

var nodeIterator = document.createNodeIterator(
  document.body,
  NodeFilter.SHOW_ELEMENT
);
var currentNode = nodeIterator.nextNode();
var previousNode = nodeIterator.previousNode();
currentNode === previousNode // true

上面代码中,currentNodepreviousNode都指向同一个的节点。

注意,遍历器返回的第一个节点,总是根节点

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的属性和方法。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。