BOM
window对象
window对象代表浏览器实例
。window对象在浏览器中有两重身份,一个是ECMAScript中的Global对象
,另一个就是浏览器窗口的JavaScript接口
。这意味着网页中定义的所有对象、变量和函数都以window作为其Global对象
Global对象作用域
通过var声明
的所有全局变量和函数
都会变成window对象的属性和方法
。如果在这里使用let或const
替代var,则不会
把变量添加给全局对象。可以在window对象
上查询是否存在可能未声明的变量。js中有很多对象暴露在全局作用域内,作为window的属性
,例如location
、history
等
窗口关系
top
指向最外层窗口,即浏览器窗口本身parent
对象指向当前窗口的父窗口,如果是最外层就等于topshelf
对象始终指向window
窗口位置与像素比
现代浏览器提供了screenLeft
和screenTop
属性,用于表示窗口相对于屏幕左侧
和顶部
的位置,返回值的单位是CSS像素
窗口大小
innerWidth、innerHeight、outerWidth和outerHeight。outerWidth和outerHeight返回浏览器窗口自身的大小
(不管是在最外层window上使用,还是在窗格
中使用)。innerWidth和innerHeight返回浏览器窗口中页面视口
的大小(不包含
浏览器边框和工具栏)。document.documentElement.clientWidth和document.documentElement.clientHeight返回页面视口的宽度和高度
。
视口位置
度量文档相对于视口滚动距离
的属性有两对,返回相等的值:window.pageXoffset/window. scrollX和window.pageYoffset/window.scrollY。可以使用scroll()、scrollTo()和scrollBy()方法滚动页面。
导航与打开新窗口
window.open()
方法可以用于导航到指定URL
,也可以用于打开新浏览器窗口
。这个方法接收4个参数:要加载的URL、目标窗口、特性字符串和表示新窗口在浏览器历史记录中是否替代当前加载页面的布尔值。
定时器
JavaScript在浏览器中是
单线程
执行的,但允许使用定时器
指定在某个时间之后
或每隔一段时间
就执行相应的代码。setTimeout()用于指定在一定时间后执行某些代码,而setInterval()用于指定每隔一段时间执行某些代码。
setTimeout()
setTimeout()方法通常接收两个参数:要执行的代码
和在执行回调函数前等待的时间(毫秒)
。不通常的:setTimeout接受从第三个参数之后的所有参数
并且传递作为函数入参
传递给第一个参数
(如果第一个参数是函数)this指针
。所有超时执行的代码
(函数)都会在全局作用域
中的一个匿名函数中运行,因此函数中的this值在非严格模式下始终指向window
,而在严格模式下是undefined
。如果给setTimeout()提供了一个箭头函数
,那么this会保留为定义
它时所在的词汇作用域。
第二个参数是要
等待的毫秒数
,而不是要执行代码的确切时间。
JavaScript是单线程
的,所以每次只能执行一段代码
。为了调度不同代码的执行,JavaScript维护了一个任务队列
。其中的任务会按照添加到队列的先后顺序
执行。setTimeout()的第二个参数
只是告诉JavaScript引擎
在指定的毫秒数过后
把任务添加到这个队列
。如果队列是空的
,则会立即执行该代码
。如果队列不是空的,则代码必须等待前面的任务
执行完才能执行。
调用setTimeout()时,会返回一个表示该超时排期的数值ID
。这个超时ID
是被排期执行代码的唯一标识符
,可用于取消该任务
。要取消等待中的排期任务,可以调用clearTimeout()方法
并传入超时id
let timeoutId = setTimeout(() => alert("Hello world! "), 1000);
// 取消超时任务
clearTimeout(timeoutId);
location对象
location是最有用的BOM对象之一,提供了
当前窗口中加载文档
的信息,以及通常的导航功能
。这个对象独特的地方在于,它既是window的属性
,也是document的属性
。也就是说,window.location和document.location指向同一个对象。
假设浏览器当前加载的URL是http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q=javascript#contents, location对象的内容如下表所示
属性 | 值 | 意义 |
---|---|---|
location.hash | “#contents ” | URL散列值,#号后面跟0或多个字符串,如果没有则为空字符串 |
location.host | www.wrong.com:80 | 服务器名及端口号 |
location.hostname | www.wrong.com | 服务器名 |
location.href | http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q=javascript#contents | 当前加载页面的完整URL。location的toString方法返回这个值 |
location.pathname | /WileyCDA/ | 资源路径名 |
location.port | 80 | 端口号 |
location.protocol | http | 协议 |
location.search | ?q=javascript | 查询字符串 |
location.username | foouser | 域前用户名 |
location.password | barpassword | 域前指定密码 |
location.origin | http://www.wrong.com | URL源地址 |
查询字符串
location.search返回了从问号开始直到URL末尾的所有内容,
let getQueryStringArgs = function() {
// 取得没有开头问号的查询字符串
let qs = (location.search.length > 0 ? location.search.substring(1) : ""),
// 保存数据的对象
args = {};
// 把每个参数添加到args对象
for (let item of qs.split("&").map(kv => kv.split("="))) {
let name = decodeURIComponent(item[0]),
value = decodeURIComponent(item[1]);
if (name.length) {
args[name] = value;
}
}
return args;
}
*** 作地址
可以通过修改location对象
修改浏览器的地址
。首先,最常见的是使用assign()
方法并传入一个URL,location.assign(“http://www.wrox.com”);这行代码会立即启动导航到新URL
的 *** 作,同时在浏览器历史记录
中增加一条记录。如果给location.href或window.location设置一个URL
,也会以同一个URL值调用assign()方法。修改location对象的属性
也会修改当前加载
的页面。其中,hash、search、hostname、pathname和port属性
被设置为新值
之后都会修改当前URL
,除了hash
之外,只要修改location的一个属性
,就会导致页面重新加载新URL
。注意 修改hash的值会在浏览器历史中
增加一条新记录如果不希望增加历史记录
,可以使用replace()
方法。这个方法接收一个URL参数
,但重新加载后不会增加历史记录
。调用replace()之后,用户不能回到前一页
。最后一个修改地址的方法是reload()
,它能重新加载当前显示的页面
。调用reload()而不传参数
,页面会以最有效的方式重新加载
。如果页面自上次请求以来没有修改
过,浏览器可能会从缓存中加载页面
。如果想强制从服务器重新加载
,可以给reload()传个true
navigator对象
只要浏览器启用JavaScript
, navigator对象
就一定存在。但是与其他BOM对象一样,每个浏览器都支持自己的属性。navigator对象的属性通常用于确定浏览器的类型。
检测插件
navigator.plugins
*** 作系统
navigator.platform
navigator.platform属性是一个字符串,通常表示浏览器所在的 *** 作系统。
screen对象
window的另一个属性screen对象
,是为数不多的几个在编程中很少用
的JavaScript对象。这个对象中保存的纯粹是客户端能力信息,也就是浏览器窗口外面
的客户端显示器的信息,比如像素宽度和像素高度。
history对象
history对象表示当前窗口首次使用以来用户的导航历史记录
。因为history是window的属性,所以每个window都有自己的history对象。
导航
go()方法
可以在用户历史记录中沿任何方向导航,可以前进也可以后退。这个方法只接收一个参数,这个参数可以是一个整数,表示前进或后退多少步。负值表示在历史记录中后退(类似点击浏览器的“后退”按钮),而正值表示在历史记录中前进(类似点击浏览器的“前进”按钮)go()有两个简写方法:back()和forward()
。顾名思义,这两个方法模拟了浏览器的后退按钮和前进按钮:
length属性
history对象还有一个length属性
,表示历史记录中有多个条目
。这个属性反映了历史记录的数量
,包括可以前进和后退的页面。注意如果页面URL发生变化
,则会在历史记录中生成一个新条目
。对于2009年以来发布的主流浏览器,这包括改变URL的散列值
(因此,把location.hash设置为一个新值会在这些浏览器的历史记录中增加一条记录)。这个行为常被单页应用程序框架
用来模拟前进和后退
,这样做是为了不会因导航而触发页面刷新
。
历史状态管理
state
小结
浏览器对象模型(BOM, Browser Object Model)是以window对象
为基础的,这个对象代表了浏览器窗口和页面可见的区域
。
location对象
可以以编程方式 *** 纵浏览器的导航系统
。通过设置这个对象上的属性,可以改变浏览器URL中的某一部分或全部。使用replace()方法可以替换浏览器历史记录中当前显示的页面,并导航到新URL。navigator对象提供关于浏览器
的信息。提供的信息类型取决于浏览器,不过有些属性如userAgent是所有浏览器
都支持的。BOM中的另外两个对象也提供了一些功能。screen对象中保存着客户端显示器
的信息。这些信息通常用于评估浏览网站的设备信息。history对象提供了 *** 纵浏览器历史
记录的能力,开发者可以确定历史记录中包含多少个条目,并以编程方式实现在历史记录中导航,而且也可以修改历史记录。
客户端检测
能力检测
能力检测(又称特性检测)即在JavaScript运行时中使用一套简单的检测逻辑
,测试浏览器是否支持某种特性
。能力检测最适合用于决定下一步
该怎么做,而不一定能够作为辨识浏览器的标志
。
用户代理检测
用户代理检测通过浏览器的用户代理字符串
确定使用的是什么浏览器。用户代理字符串包含在每个HTTP请求的头部
,在JavaScript中可以通过navigator.userAgent
访问。
用户代理的历史
HTTP规范(1.0和1.1)要求浏览器应该向服务器发送包含浏览器名称和版本信息的简短字符串。
Mosaic
是早期Web浏览器的代表,其用户代理字符串相当简单,类似于:Mosaic/0.9Netscape Navigator 3和IE3。1996年,Netscape Navigator 3发布之后超过Mosaic成为最受欢迎的浏览器。Mozilla
/Version(Platform; Encryption[; OS-or-CPUdescription])3.Netscape Communicator 4和IE4~8Gecko。Gecko渲染引擎是Firefox的核心。Gecko最初是作为通用Mozilla浏览器(即后来的Netscape 6)的一部分开发的。有一个针对Netscape 6的用户代理字符串规范,规定了未来的版本应该如何构造这个字符串。WebKit
。2003年,苹果宣布将发布自己的浏览器Safari。Safari的渲染引擎叫WebKit,是基于Linux平台浏览器Konqueror使用的渲染引擎KHTML开发的。Chrome
。谷歌的Chrome浏览器使用Blink作为渲染引擎,使用V8作为JavaScript引擎。Chrome的用户代理字符串包含所有WebKit的信息,另外又加上了Chrome及其版本的信息。
浏览器分析
想要知道自己代码运行在什么浏览器上,大部分开发者会分析window.navigator.userAgent返回
的字符串值。所有浏览器都会提供这个值,如果相信这些返回值并基于给定的一组浏览器检测这个字符串,最终会得到关于浏览器和 *** 作系统的比较精确的结果。相比于能力检测,用户代理检测还是有一定优势的。能力检测可以保证脚本不必理会浏览器而正常执行。现代浏览器用户代理字符串使我们能够利用它们准确识别浏览器
。
小结
能力检测
,在使用之前先测试浏览器的特定能力
。例如,脚本可以在调用某个函数之前先检查它是否存在。这种客户端检测方式可以让开发者不必考虑特定的浏览器或版本,而只需关注某些能力
是否存在。能力检测不能精确地反映特定的浏览器或版本。用户代理检测
,通过用户代理字符串确定浏览器。用户代理字符串包含关于浏览器的很多信息
,通常包括浏览器、平台、 *** 作系统和浏览器版本。用户代理字符串有一个相当长的发展史,很多浏览器都试图欺骗网站相信自己是别的浏览器。用户代理检测也比较麻烦,特别是涉及Opera会在代理字符串中隐藏自己信息
的时候。即使如此,用户代理字符串也可以用来确定浏览器使用的渲染引擎以及平台,包括移动设备和游戏机。选择客户端检测方法时,首选是使用能力检测
。特殊能力检测要放在次要位置,作为决定代码逻辑的参考。用户代理检测是最后一个选择,因为它过于依赖用户代理字符串。
DOM
文档对象模型(DOM, Document Object Model)是HTML和XML文档
的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档。DOM表示由多层节点构成的文档
,通过它开发者可以添加、删除和修改页面
的各个部分。
节点层级
document节点
表示每个文档的根节点
。在HTML中,根节点的唯一子节点
是元素
,我们称之为文档元素(documentElement)
。文档元素是文档最外层的元素
,所有其他元素都存在于这个元素之内。每个文档只能有一个文档元素
。在HTML页面中,文档元素始终是元素
。
HTML中的每段标记都可以表示为这个树形结构中的一个节点
。元素节点
表示HTML元素
,属性节点
表示属性
,文档类型节点
表示文档类型
,注释节点
表示注释
。DOM中总共有12种节点类型,这些类型都继承一种基本类型
。
Node类型
Node接口是所有DOM节点类型
都必须实现的。Node接口在JavaScript中被实现为Node类型,JavaScript中,所有节点类型都继承Node类型
,因此所有类型都共享相同的基本属性和方法。
值属性
nodetype
属性,表示节点的类型
,定义了12个常量(如Node.ELEMENT_NODE(1)、Node.ATTRIBUTE_NODE(2)…),结点类型可以通过与这些常量比较来确定nodeName
属性,节点的标签名字
nodeValue
属性,对于一个元素
来说,nodeName是标签名,nodeValue是null。
关系属性(关系指针)和方法
childNodes
属性,其中包含一个NodeList实例
,是一个类数组对象
,可以用中括号访问,也可以用item()访问,具有length属性
。NodeLisy对象独特的地方在于它其实是对一个DOM结构的查询,因此DON结构的变化会自动
地在NodeList中反映
出来,我们通常说NodeList是实时的活动对象
,而不是第一次访问时所获得的内容的快照
let firstChild = someNode.childNodes[0];
let secondChild = someNode.childNodes.item(1);
let count = someNode.childNodes.length;
parentNode
属性,指向DOM树中的父元素
。childNodes
中所有的节点都有同一个父元素
,因此它们的parentNode属性
都指向同一个节点
。childNodes列表中的每个节点都是同一列表
中其他节点的同胞节点
。而使用previousSibling
和nextSibling
可以在这个列表的节点间导航
。firstChild
和lastChild
属性。父节点
和它的第一个及最后一个子节点
也有专门属性:firstChild和lastChild分别指向childNodes
中的第一个和最后一个子节点
。someNode.firstChild
的值始终等于someNode.childNodes[0]
,而someNode.lastChild
的值始终等于someNode.childNodes[someNode.childNodes.length-1]
。如果只有一个子节点
,则firstChild和lastChild指向同一个节点
。如果没有子节点
,则firstChild和lastChild都是null
。hasChildNodes()
,这个方法如果返回true则说明节点有一个或多个子节点。比使用length更方便ownerDocument
属性是一个指向代表整个文档
的文档节点的指针。即返回document对象
*** 纵节点
appendChild()
,用于在childNodes列表
末尾添加
节点。添加新节点会更新相关的关系指针
,包括父节点
和之前的最后一个子节点。appendChild()方法
返回新添加的节点
,如下所示:如果把文档中已经存在的节点
传给appendChild()
,则这个节点会从之前的位置被转移
到新位置,一个节点不会在文档中同时出现两个或者更多地方,如果调用该方法传入父元素的第一个子节点,则该结点会成为父元素的最后一个节点。
let returnedNode = someNode.appendChild(someNode.firstChild);
alert(returnedNode == someNode.firstChild); // false
alert(returnedNode == someNode.lastChild); // true
insertBefore()
方法。这个方法接收两个参数:要插入的节点
和参照节点
。插入的节点会变成参照节点的前一个同胞节点,并被返回。appendChild()和insertBefore()在插入节点时不会删除任何已有节点
。replaceChild()
方法接收两个参数:要插入的节点
和要替换的节点
。要替换的节点
会被返回
并从文档树中完全移除
,要插入的节点
会取而代之
。使用replaceChild()插入一个节点后,所有关系指针
都会从被替换的节点复制过来。
// 替换第一个子节点
let returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
// 替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
removeChild()
方法。这个方法接收一个参数
,即要移除的节点
。被移除的节点会被返回。上面介绍的
4个方法都用于 *** 纵某个节点的子元素
,也就是说使用它们之前必须先取得父节点
(使用前面介绍的parentNode属性)。
其他方法
cloneNode()
,会返回与调用它的节点一模一样的节点。cloneNode()方法接收一个布尔值参数,表示是否深复制
。在传入true参数时,会进行深复制,即复制节点及其整个子DOM树
。如果传入false
,则只会复制调用该方法的节点。复制返回的节点属于文档
所有,但尚未指定父节点
,所以可称为孤儿节点
(orphan)。可以通过appendChild()、insertBefore()或replaceChild()方法把孤儿节点添加到文档中。cloneNode()方法不会复制添加到DOM节点的JavaScript属性
,比如事件处理程序。这个方法只复制HTML属性
,以及可选地复制子节点。normalize()
。这个方法唯一的任务就是处理文档子树中的文本节点,处理不包含文本的文本节点
document类型
Document类型
是JavaScript中表示文档节点
的类型。document
是window对象的属性,因此是一个全局对象
。document对象可用于获取关于页面的信息以及 *** 纵其外观和底层结构
。
访问文档子节点
documentElement属性
,始终指向HTML页面中的元素
。
let html = document.documentElement;
// 取得对的引用
alert(html === document.childNodes[0]); // true
alert(html === document.firstChild); // true
body属性
,直接指向元素
。document.body
let body = document.body;
doctype属性
,标签
是文档中独立的部分,其信息可以通过doctype属性
(在浏览器中是document.doctype)来访问
文档信息
title
,包含元素
中的文本,通常显示在浏览器窗口
或标签页
的标题栏。通过这个属性可以读写页面的标题
,修改后的标题也会反映在浏览器标题栏上。
// 修改文档标题
document.title = "New page title";
URL
,包含当前页面的完整URL(地址栏中的URL)domain
,包含页面的域名referrer
,包含链接到当前页面的那个页面的URL。如果当前页面没有来源,则referrer属性包含空字符串。所有这些信息
都可以在请求的HTTP头部信息
中获取,只是在JavaScript
中通过这几个属性暴露
出来而已
// 取得完整的URL
let url = document.URL;
// 取得域名
let domain = document.domain;
// 取得来源
let referrer = document.referrer;
URL
跟域名
是相关的。比如,如果document.URL
是http://www.wrox.com/WileyCDA/
,则document.domain
就是www.wrox.com
。这些属性中只有域名domain是可以修改,可以将域名修改为原域名中的子域名,不能给他设置为原域名中不包含的值,且一旦缩紧就不能够放松
定位(获取)元素
使用DOM最常见的情形可能就是获取某个或某组元素的引用
,然后对它们执行某些 *** 作。
getElementById()
方法接收一个参数,即要获取元素的ID
,如果找到了则返回这个元素,如果没找到则返回null。参数ID
必须跟元素在页面中的id属性值
完全匹配,包括大小写
。如果页面中存在多个具有相同ID
的元素,则getElementById()返回在文档中出现的第一个元素
。getElementsByTagName()
是另一个常用来获取元素引用的方法。这个方法接收一个参数,即要获取元素的标签名
,返回包含零个或多个元素的NodeList
。这个方法返回一个HTMLCollection对象,与NodeList相似,他还有一个namedItem()
方法来通过标签的name属性取得某一项的引用要取得文档中的所有元素
,可以给getElementsByTagName()
传入*。在JavaScript和CSS中,*一般被认为是匹配一切的字符。getElementsByName()
。顾名思义,这个方法会返回具有给定name属性的所有元素。getElementsByName()方法最常用于单选按钮
,因为同一字段的单选按钮必须具有相同的name属性才能确保把正确的值发送给服务器,
DOM兼容性检测
document.implementation属性
是一个对象,其中提供了与浏览器DOM实现相关的信息和能力。DOM Level 1在document.implementation上只定义了一个方法,即hasFeature()
。这个方法接收两个参数:特性名称和DOM版本。如果浏览器支持指定的特性和版本,则hasFeature()方法返回true,如下面的例子所示:
let hasXmlDom = document.implementation.hasFeature("XML", "1.0");
文档写入
网页输出流中写入内容。这个能力对应4个方法:
write()writeln()open()close()其中,
write()和writeln()方法
都接收一个字符串参数,可以将这个字符串写入网页中。write()简单地写入文本,而writeln()还会在字符串末尾追加一个换行符(\n)。这两个方法可以用来在页面加载期间
向页面
中动态添加内容
。
document.write("" + (new Date()).toString() + "");
write()和writeln()方法经常用于动态包含外部资源
,如JavaScript文件。在包含JavaScript文件时,记住不能这样直接包含字符串""
,因为这个字符串会被解释为脚本块的结尾,
Element类型
除了Document类型,Element类型就是Web开发中最常用的类型
了。Element表示XML
或HTML元素
,对外暴露出访问元素标签名、子节点和属性
的能力。
可以通过nodeName或tagName属性来获取元素的标签名。这两个属性返回同样的值。先把标签名转换为全部小写后再比较。
HTML元素
继承的属性
HTMLElement直接继承Element并增加了一些属性。
id
,元素在文档中的唯一标识符;title,包含元素的额外信息,通常以提示条形式展示;lang,元素内容的语言代码(很少用);dir,语言的书写方向("ltr"表示从左到右,"rtl"表示从右到左,同样很少用);className
,相当于class属性,用于指定元素的CSS类(因为class是ECMAScript关键字,所以不能直接用这个名字)。
let div = document.getElementById("myDiv");
alert(div.id);
// "myDiv"
alert(div.className);
// "bd"
alert(div.title);
// "Body text"
alert(div.lang);
// "en"
alert(div.dir);
// "ltr"`
取得属性
每个元素都有零个或多个属性
,通常用于为元素或其内容附加更多信息。与属性相关的DOM方法主要有3个:getAttribute()、setAttribute()和removeAttribute()
。这些方法主要用于 *** 纵属性
,包括在HTMLElement类型上定义的属性
let div = document.getElementById("myDiv");
alert(div.getAttribute("id"));
// "myDiv"
alert(div.getAttribute("class"));
// "bd"
alert(div.getAttribute("title"));
// "Body text"
alert(div.getAttribute("lang"));
// "en"
alert(div.getAttribute("dir"));
// "ltr"
通过DOM对象访问的属性
中有两个返回的值跟使用getAttribute()
取得的值不一样。首先是style属性
,这个属性用于为元素设定CSS样式
。在使用getAttribute()访问style属性
时,返回的是CSS字符串
。而在通过DOM对象的属性访问
时,style属性返回的是一个(CSSStyleDeclaration)对象
。DOM对象的style属性用于以编程方式读写元素样式,因此不会直接映射为元素中style属性的字符串值。
第二个属性其实是一类,即事件处理程序(或者事件属性)
,比如onclick
。在元素上
使用事件属性时(比如onclick),属性的值
是一段JavaScript
代码。如果使用getAttribute()
访问事件属性
,则返回的是字符串形式的源代码
。而通过DOM对象的属性
访问事件属性
时返回的则是一个JavaScript函数
(未指定该属性则返回null)。这是因为onclick及其他事件属性是可以接受函数作为值的。
考虑到以上差异,开发者在进行DOM编程
时通常会放弃使用getAttribute()
而只使用对象属性
。getAttribute()主要用于取得自定义属性的值。
setAttribute()
,这个方法接收两个参数:要设置的属性名和属性的值
。如果属性已经存在,则setAttribute()会以指定的值替换
原来的值;如果属性不存在
,则setAttribute()会以指定的值创建该属性
。
attributes属性
Element类型
是唯一使用attributes属性
的DOM节点类型。attributes属性包含一个NamedNodeMap
实例,是一个类似NodeList的“实时”集合。元素的每个属性都表示为一个Attr节点,并保存在这个NamedNodeMap对象中
创建元素
可以使用document.createElement()方法
创建新元素。这个方法接收一个参数,即要创建元素的标签名
。
let div = document.createElement("div");
使用createElement()方法
创建新元素的同时也会将其ownerDocument属性设置为document
。此时,可以再为其添加属性、添加更多子元素
。比如:
div.id = "myNewDiv";
div.className = "box";
在新元素上设置这些属性只会附加信息
。因为这个元素还没有添加到文档树
,所以不会影响浏览器显示
。要把元素添加到文档树
,可以使用appendChild()、insertBefore()或replaceChild()
。比如,以下代码会把刚才创建的元素添加到文档的元素中:
document.body.appendChild(div);
元素被添加到文档树
之后,浏览器会立即将其渲染
出来。之后
再对这个元素所做的任何修改
,都会立即在浏览器中反映出来。
元素后代
childNodes属性
包含元素所有的子节点,这些子节点可能是其他元素、文本节点、注释或处理指令
。考虑到这种情况,通常在执行某个 *** 作之后需要先检测一下节点的nodeType
,如下所示:
for (let i = 0, len = element.childNodes.length; i < len; ++i) {
if (element.childNodes[i].nodeType == 1) {
// 执行某个 *** 作
}
}
以上代码会遍历某个元素的子节点
,并且只在nodeType等于1
(即Element节点)时执行某个 *** 作。
要取得某个元素的子节点和其他后代节点
,可以使用元素的getElementsByTagName()
方法。在元素上调用这个方法与在文档上调用是一样的,只不过搜索范围限制在当前元素之内,即只会返回当前元素的后代
。对于前面
的例子,可以像下面这样取得其所有的元素
:
let ul = document.getElementById("myList");
let items = ul.getElementsByTagName("li");
这里例子中的元素
只有一级子节点
,如果它包含更多层级,则所有层级中的元素
都会返回。
Text类型
Text节点由Text类型表示,包含按字面解释的纯文本,Text类型的节点具有以下特征:
nodeType等于3;nodeName值为"#text";nodeValue值为节点中包含的文本;parentNode值为Element对象;不支持子节点。Text节点中包含的文本可以通过
nodeValue属性
访问,也可以通过data属性
访问,
DocumentType类型
DocumentType类型的节点包含文档的文档类型(doctype)信息,具有以下特征:
nodeType等于10;nodeName值为文档类型的名称;nodeValue值为null;parentNode值为Document对象;不支持子节点。DocumentType对象保存在
document.doctype
属性中。
DOM编程
动态脚本
元素用于向网页中插入JS代码,可以是arc属性包含的外部文件,也可以是作为该元素内容的源代码。注意通过innerHTML属性创建的
元素永远不会执行。
动态样式
css样式在HTML页面中可以通过两个元素加载,元素用于包含CSS外部文件,
元素用于添加嵌入样式。动态样式也是页面初始加载时并不存在,而是在之后才添加到页面中的
使用NodeList
理解NodeList对象和相关的NamedNodeMap、HTMLCollection
是理解DOM编程的关键。这三个集合类型都是实时的
,意味着文档结构的变化会实时的在他们身上反映出来,他们的值始终代表最新的状态。因此最好限制操作NodeList的次数,因为每次查询都会搜索整个文档,最好把查询到的NodeList缓存起来。
MutationObserver接口
MutationObserver接口可以在DOM被修改时异步执行回调,使用它可以观察整个文档、DOM树的一部分
observer()方法
//创建一个观察者并配置它观察body元素上的属性变化
let observer = new MutationObserver(() => console.log(' attributes changed'));
observer.observe(document.body,{attributes : true});
首先通过调用构造函数
并传入一个回调函数
来创建MutationObserver实例
,该实例还不会关联DOM
的任何部分,要把它与DOM关联起来,需要使用observe()方法
,接收两个参数
:要观察其变化的DOM结点
以及一个MutationObserverInit
对象,MutationObserverInit对象用于控制观察哪些方面
的变化,是一个键值对
形式配置选项的字典。
执行上述代码后,body元素
上任何属性
发生变化都会被这个MutationObserver实例发现。需要注意的是,回调中的console.log是后执行
的,表明回调并非与实际的DOM变化同步执行
小结
DOM由一系列节点类型构成,主要包括以下几种。
Node
是基准节点类型,是文档一个部分的抽象表示,所有其他类型都继承Node。Document
类型表示整个文档,对应树形结构的根节点。在JavaScript中,document对象是Document的实例
,拥有查询和获取节点的很多方法。Element
节点表示文档中所有HTML或XML元素,可以用来操作它们的内容和属性。其他节点类型分别表示文本内容、注释、文档类型、CDATA区块
和文档片段。DOM编程在多数情况下没什么问题,在涉及