DOM模型(二)—— Document节点

一、概述


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

document节点有不同的办法可以获取。

  • 对于正常的网页,直接使用documentwindow.document
  • 对于iframe载入的网页,使用iframe节点的contentDocument属性。
  • 对于Ajax操作返回的文档,使用XMLHttpRequest对象的responseXML属性。
  • 对于已知包含某个节点的文档,使用该节点的ownerDocument属性。

上面得到的四种document节点,都部署了Document接口,因此有共同的属性和方法。当然,各自也有一些自己独特的属性和方法,比如HTML和XML文档的document节点就不一样。

二、指向文档内部节点的属性


document节点有很多属性,其中相当一部分属于快捷方式,指向文档内部的某个节点。

2.1、document.doctype、document.documentElement、document.defaultView

对于HTML文档来说,document对象一般有两个子节点。第一个子节点是document.doctype,它是一个对象,包含了当前文档类型(Document Type Declaration,简写DTD)信息。对于HTML5文档,该节点就代表<!DOCTYPE html>。如果网页没有声明DTD,该属性返回null

var doctype = document.doctype;
doctype  //<!DOCTYPE html>
doctype.name  //"html"

document.firstChild通常就返回这个节点。

document.documentElement属性返回当前文档的根节点(root)。它通常是document节点的第二个子节点,紧跟在document.doctype节点后面。对于HTML网页,该属性返回<html>节点。

document.defaultView属性,在浏览器中返回document对象所在的window对象,否则返回null

document.defaultView === window // true
验证

2.2、document.body、document.head

document.head属性返回当前文档的<head>节点,document.body属性返回当前文档的<body>节点。

document.head === document.querySelector('head')  //true
document.body === document.querySelector('body')  //true

这两个属性总是存在的,如果网页源码里面省略了<head><body>,浏览器会自动创造。另外,这两个属性是可写的,如果对其写入一个新的节点,会导致原有的所有子节点被移除。

2.3、document.activeElement

document.activeElement属性返回当前文档中获得焦点的那个元素。用户通常可以使用Tab键移动焦点,使用空格键激活焦点。比如,如果焦点在一个链接上,此时按下一个空格键,就会跳转到该链接。

三、返回特定节点集合的属性


以下属性返回文档内部特定元素的集合,都是类似数组的对象。这些集合都是动态的,原节点有任何变化,立刻会反映在集合中

3.1、document.links、document.forms、document.images、document.embeds

document.links属性返回当前文档所有设定了href属性的aarea元素。

document.forms属性返回页面中所有表单元素的form

var selectForm = document.forms[0];

上面代码获取文档第一个表单。

document.images属性返回页面所有图片元素(即img标签)。

var imglist = document.images;
for(var i=0;i<imglist.length;i++){
    if(imglist[i].src === 'banner.gif'){
        //...
    }
}

上面代码在所有img标签中,寻找特定图片。

document.embeds属性返回网页中所有嵌入对象,即embed标签。

以上四个属性返回的都是HTMLCollection对象实例。

验证

由于HTMLCollection对象的实例可以用HTML元素的id或name属性引用,因此如果一个元素有id或name属性,就可以在上面这四个属性上引用。

//HTML代码
<form name="myForm">

//JS
document.myForm === document.forms.myForm  //true

3.2、document.scripts、document.styleSheets

document.scripts属性返回当前文档的所有脚本(即script标签)。

var scripts = document.scripts;
if(scripts.length !== 0){
    console.log('当前网页有脚本');
}

document.scripts属性返回的也是HTMLCollection对象的实例。
因此,如果一个script标签有idname属性,就可以在document.scripts上引用。

document.styleSheets属性返回一个类似数组的对象,代表当前网页的所有样式表。每个样式表对象都有cssRules属性,返回该样式表的所有CSS规则,这样就可以操作具体的CSS规则了。

四、返回文档信息的属性


以下属性返回文档信息。

4.1、document.documentURI,document.URL

document.documentURI属性和document.URL属性都返回一个字符串,表示当前文档的网址。不同之处是documentURI属性是所有文档都具备的,URL属性则是HTML文档独有的。

document.documentURI === document.URL
// true

另外,如果文档的锚点(#anchor)变化,这两个属性都不会跟着变化,它们的值是静态的。但是,document.location会跟着变化,document.location总是返回最新的URL,会跟踪锚点的变化。

4.2、document.domain

document.domain属性返回当前文档的域名。比如,某张网页的网址是 http://www.example.com/hello.html ,domain属性就等于www.example.com。如果无法获取域名,该属性返回null

var badDomain = 'www.example.xxx';
if (document.domain === badDomain)
  window.close();

上面代码判断,如果当前域名等于指定域名,则关闭窗口。

二级域名的情况下,domain属性可以设置为对应的一级域名。比如,当前域名是sub.example.com,则domain属性可以设置为example.com。除此之外的写入,都是不可以的。

4.3、document.lastModified

document.lastModified属性返回当前文档最后修改的时间戳,格式为字符串。

document.lastModified
// Tuesday, July 10, 2001 10:19:42

注意,lastModified属性的值是是字符串,所以不能用来直接比较,两个文档谁的日期更新,需要用Date.parse方法转成时间戳格式,才能进行比较。

4.4、document.location

document.location属性返回location对象,提供了当前文档的URL信息。

// 当前网址为 http://user:passwd@www.example.com:4097/path/a.html?x=111#part1
document.location.href // "http://user:passwd@www.example.com:4097/path/a.html?x=111#part1"
document.location.protocol // "http:"
document.location.host // "www.example.com:4097"
document.location.hostname // "www.example.com"
document.location.port // "4097"
document.location.pathname // "/path/a.html"
document.location.search // "?x=111"
document.location.hash // "#part1"
document.location.user // "user"
document.location.password // "passed"

location对象有以下方法。

location.assign()
location.reload()
location.toString()

// 跳转到另一个网址
document.location.assign('http://www.google.com')
// 优先从服务器重新加载
document.location.reload(true)
// 优先从本地缓存重新加载(默认值)
document.location.reload(false)
// 跳转到新网址,并将取代掉history对象中的当前记录
document.location.replace('http://www.google.com');
// 将location对象转为字符串,等价于document.location.href
document.location.toString()

如果将新的网址赋值给location对象,网页就会自动跳转到新网址。

document.location = 'http://www.example.com';
// 等同于
document.location.href = 'http://www.example.com';

也可以指定相对URL。

document.location = 'page2.html';

如果指定的是锚点,浏览器会自动滚动到锚点处。

document.location = '#top';

注意,采用上面的方法重置URL,跟用户点击链接跳转的效果是一样的。上一个网页依然将保存在浏览器历史之中,点击“后退”按钮就可以回到前一个网页。

如果不希望用户看到前一个网页,可以使用location.replace方法,浏览器history对象就会用新的网址,取代当前网址,这样的话,“后退”按钮就不会回到当前网页了。它的一个应用就是,当脚本发现当前是移动设备时,就立刻跳转到移动版网页。

document.location属性与window.location属性等价。

document.location === window.location // true

历史上,IE曾经不允许对document.location赋值,为了保险起见,建议优先使用window.location。如果只是单纯地获取当前网址,建议使用document.URL,语义性更好。

4.5、document.referrer、document.title、document.characterSet

document.referrer属性返回一个字符串,表示当前文档的访问来源,如果是无法获取来源或是用户直接键入网址,而不是从其他网页点击,则返回一个空字符串。

document.referrer的值,总是与HTTP头信息的Referer保持一致,但是它的拼写有两个r。

document.title属性返回当前文档的标题,该属性是可写的。

document.title = '新标题';

document.characterSet属性返回渲染当前文档的字符集,比如UTF-8、ISO-8859-1

4.6、document.readyState

document.readyState属性返回当前文档的状态,共有三种可能的值。

loading:加载HTML代码阶段(尚未完成解析)
interactive:加载外部资源阶段时
complete:加载完成时

这个属性变化过程如下。

1、浏览器开始解析HTML文档,document.readyState属性等于loading。
2、浏览器遇到HTML文档中的<script>元素,并且没有async和defer属性,就暂停解析,开始执行脚本,这时document.readyState属性还是loading。
3、HTML文档解析完成,document.readyState属性变成interactive。
4、浏览器等待图片、样式表、字体文件等外部资源加载完成,一旦全部加载完成,document.readyState属性变成complete。

4.7、document.designMode

document.designMode属性控制当前文档是否可编辑,通常用在制作所见即所得编辑器。打开iframe元素包含的文档的designMode属性,就能将其变为一个所见即所得的编辑器。

4.8、document.implementation

document.implementation属性返回一个对象,用来甄别当前环境部署了哪些DOM相关接口。implementation属性的hasFeature方法,可以判断当前环境是否部署了特定版本的特定接口。

document.implementation.hasFeature('HTML', '2.0')
// true

document.implementation.hasFeature('MutationEvents','2.0')
// true

上面代码表示,当前环境部署了DOM HTML 2.0版和MutationEvents的2.0版。

4.9、document.compatMode

document.compatMode属性返回浏览器处理文档的模式,可能的值为BackCompat向后兼容模式和CSS1Compat(严格模式)。

一般来说,如果网页代码的第一行设置了明确的DOCTYPE(比如<!DOCTYPE html>),document.compatMode的值都为CSS1Compat

4.10、document.cookie

document.cookie属性用来操作浏览器Cookie。

五、读写相关的方法


5.1、document.open(),document.close()

document.open方法用于新建一个文档,供write方法写入内容。它实际上等于清除当前文档,重新写入内容。不要将此方法与window.open()混淆,后者用来打开一个新窗口,与当前文档无关。

document.close方法用于关闭open方法所新建的文档。一旦关闭,write方法就无法写入内容了。如果再调用write方法,就等同于又调用open方法,新建一个文档,再写入内容。

5.2、document.write(),document.writeIn()

document.write方法用于向当前文档写入内容。只要当前文档还没有用close方法关闭,它所写入的内容就会追加在已有内容的后面。

// 页面显示“helloworld”
document.open();
document.write('hello');
document.write('world');
document.close();

注意,document.write会当作HTML代码解析,不会转义。

document.write('<p>hello world</p>');

如果页面已经解析完成(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是JS语言标准化之前就存在的方法,现在完全有更符合标准的方法向文档写入内容(比如对innerHTML属性赋值)。所以,除了某些特殊情况,应该尽量避免使用document.write这个方法。

document.writeIn方法与write方法完全一致,除了会在输出内容的尾部添加换行符。

document.write(1);
document.write(2);
// 12

document.writeln(1);
document.writeln(2);
// 1
// 2
//

注意,writeIn方法添加的是ASCII码换行符,渲染成HTML网页时不起作用,即在网页上显示不出换行。

六、查找节点的方法


以下方法用来查找某个节点

6.1、document.querySelector(),document.querySelectorAll()

document.querySelector方法接受一个CSS选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null

var el1 = document.querySelector('.myclass');
var el1 = document.querySelector('#myclass > [ng-click]');

document.querySelectorAll方法与querySelector用法类似,区别是返回一个NodeList对象,包含所有匹配给定选择器的节点。

elementList = document.querySelectorAll('.myclass');

这两个方法的参数,可以是逗号分隔的多个CSS选择器,返回能够匹配其中的选择器的所有元素节点。

var matches = document.querySelectorAll('div.note, div.alert');

上面代码返回一个文档中所有的class为"note"或者 "alert"的div元素。

验证

这两个方法都支持复杂的CSS选择器,但是,它们不支持CSS伪元素的选择器和伪类的选择器,即无法选中伪元素和伪类。

如果querySelectorAll方法的参数是字符串*,则会返回文档中的所有HTML元素节点。另外,querySelectorAll的返回结果不是动态集合,不会实时反映元素节点的变化。

最后,这两个方法除了定义在document对象上,还定义在元素节点上,即在元素节点上也可以调用。

6.2、document.getElementsByTagName()

document.getElementsByTagName方法返回所有指定HTML标签的元素,返回值是一个类似数组的HTMLCollection对象,可以实时反映HTML文档的变化。如果没有任何匹配的元素,就返回一个空集。

var paras = document.getElementsByTagName('p');
paras instanceof HTMLCollection  //true

上面代码返回当前文档的所有p元素节点。

验证

HTML标签名是大小写不敏感的,因此getElementsByTagName方法也是大小写不敏感的。另外,返回结果中,各个成员的顺序就是它们在文档中出现的顺序。

如果传入字符串*,就可以返回文档中所有HTML元素。

var allElements = document.getElementsByTagName('*');

注意,HTML元素本身也定义了getElementsByTagName方法,返回该元素的后代元素中符合指定标签的元素。也就是说,这个方法不仅可以在document对象上调用,也可以在任何元素节点上调用。

var firstPara = document.getElementsByTagName('p')[0];
var spans = firstPara.getElementsByTagName('span');

上面代码选中第一个p元素内部的所有span元素。

6.3、document.getElementsByClassName()

document.getElementsByClassName方法返回一个类似数组的对象(HTMLCollection实例对象),包括了所有class名字符合指定条件的元素,元素的变化实时反映在返回结果中。

var elements = document.getElementsByClassName(names);

由于class是保留字,所以JS一律使用className表示CSS的class

如果参数是一个空格分隔的字符串,元素的class必须符合所有字符串之中所有的class才会返回。

var elements = document.getElementsByClassName('foo bar');

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

注意,正常模式下,CSS的class是大小写敏感的。

getElementsByTagName方法一样,getElementsByClassName方法不仅可以在document对象上调用,也可以在任何元素节点上调用。

6.4、document.getElementsByName()

document.getElementsByName方法用于选择拥有name属性的HTML元素(比如<form>、<radio>、<img>等),返回一个类似数组的对象(NodeList对象的实例),因为name属性相同的元素可能不止一个。

// 表单为 <form name="x"></form>
var forms = document.getElementsByName('x');
forms[0].tagName // "FORM"

6.5、document.getElementById()

document.getElementById方法返回匹配指定id属性的元素节点。如果没有发现匹配的节点,返回null

var elem = document.getElementById('para1');

注意:该方法的参数是大小写敏感的。比如,如果某个节点的id属性是main,那么document.getElementById('Main')将返回null,而不是那个节点。

document.getElementByIddocument.querySelector方法都能获取元素节点,不同之处是document.querySelector方法的参数使用CSS选择器语法,document.getElementById方法的参数是HTML标签元素的id属性。

document.getElementById('myElement')
document.querySelector('#myElement')

上面代码中,两个方法都能选中idmyElement的元素,但是getElementById()querySelector()效率高很多。

另外,这个方法只能在document对象上使用,不能在其他元素节点上使用

6.6、document.elementFromPoint()

document.elementFromPoint方法返回位于页面指定位置最上层的Element子节点。

var element = document.elementFromPoint(50, 50);

上面代码选中在(50, 50)这个坐标位置的最上层的那个HTML元素。

elementFromPoint方法的两个参数,依次是相对于当前视口左上角的横坐标和纵坐标,单位是像素。如果位于该位置的HTML元素不可返回(比如文本框的滚动条),则返回它的父元素(比如文本框)。如果坐标值无意义(比如负值或超过视口大小),则返回null

七、生成节点的方法


以下方法用于生成元素节点。

7.1、document.createElement()

document.createElement方法用来生成HTML元素节点。

var newDiv = document.createElement('div');

createElement方法的参数为元素的标签名,即元素节点的tagName属性,对HTML文档大小写不敏感。如果参数带有尖括号或是null,会报错。

document.createElement('<div>')
// DOMException: The tag name provided ('<div>') is not a valid name

7.2、document.createTextNode()

document.createTextNode方法用来生成文本节点,参数为所要生成的文本节点的内容。

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属性赋值。

7.3、document.createAttribute()

document.createAttribute方法生成一个新的属性对象节点,并返回它。

var attribute = document.createAttribute(name);

createAttribute方法的参数name,是属性的名称。

var node = document.getElementById('div1');
var a = document.createAttribute('my_attrib');
a.value = 'newVal';
node.setAttributeNode(a);

//等同于
var node = document.getElementById('div1');
node.setAttribute('my_attrib', 'newVal');

7.4、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);
});

document.body.appendChild(docfrag);

八、事件相关的方法


8.1、document.createEvent()

document.createEvent方法生成一个事件对象,该对象可以被element.dispatchEvent方法使用,触发指定事件。

var event = document.createEvent(type);

createEvent方法的参数是事件类型,比如UIEvents、MouseEvents、MutationEvents、HTMLEvents

8.2、document.addEventListener(),document.removeEventListener(),document.dispatchEvent()

以下三个方法与document节点的事件相关。这些方法都继承自EventTarget接口。

// 添加事件监听函数
document.addEventListener('click', listener, false);

// 移除事件监听函数
document.removeEventListener('click', listener, false);

// 触发事件
var event = new Event('click');
document.dispatchEvent(event);

九、其他方法


9.1、document.hasFocus()

9.2、document.createNodeIterator(),document.createTreeWalker()

9.3、document.adoptNode()

9.4、document.importNode()

9.5、document.getSelection()

(本系列下一节为 — Element节点)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 一、JS前言 (1)认识JS 也许你已经了解HTML标记(也称为结构),知道了CSS样式(也称为表示),会使用HT...
    凛0_0阅读 2,721评论 0 8
  • 一、基本概念 1.1、DOM DOM是JS操作网页的接口,全称为“文档对象模型”(Document Object ...
    周花花啊阅读 3,026评论 0 6
  • 原文 https://www.kancloud.cn/dennis/tgjavascript/241852 一、节...
    LuckyS007阅读 795评论 0 0
  • DOM(文档对象模型)是针对 HTML 和 XML 文档的一个 API。DOM 描绘了一个层次化的节点树,允许开发...
    劼哥stone阅读 716评论 8 6
  • 或许不是我们变了,而是我们越来越接近真实的自己。 ...
    寻覔阅读 482评论 1 3