JavaScript高级程序设计(下)
2021-07-16 09:00:00

BOM

包括window对象,location对象,navigator对象,screen对象,history对象.

location 对象

这个对象独特的地方在于,它既是 window 的属性,也是 document 的属性。也就是说, window.location 和 document.location 指向同一个对象。

location 对象不仅保存着当前加载文 档的信息,也保存着把 URL 解析为离散片段后能够通过属性访问的信息。

image-20211013162903155

操作地址

可以通过修改 location 对象修改浏览器的地址。最常见的是使用 assign()方法并传入一 个 URL.

1
location.assign("http://www.wrox.com");

如果给 location.href 或 window.location 设置一个 URL,也会以同一个 URL 值调用 assign()方法。比 如,下面两行代码都会执行与显式调用 assign()一样的操作:

1
2
window.location = "http://www.wrox.com"; 
location.href = "http://www.wrox.com";

在以前面提到的方式修改 URL 之后,浏览器历史记录中就会增加相应的记录。当用户单击“后退” 按钮时,就会导航到前一个页面。如果不希望增加历史记录,可以使用 replace()方法。这个方法接 收一个 URL 参数,但重新加载后不会增加历史记录。调用 replace()之后,用户不能回到前一页。比 如下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html> 
<html>
<head>
<title>You won't be able to get back here</title>
</head>
<body>
<p>Enjoy this page for a second, because you won't be coming back here.</p>
<script>
setTimeout(() => location.replace("http://www.wrox.com/"), 1000);
</script>
</body>
</html>

**最后一个修改地址的方法是 reload()**,它能重新加载当前显示的页面。调用 reload()而不传参 数,页面会以最有效的方式重新加载。也就是说,如果页面自上次请求以来没有修改过,浏览器可能会 从缓存中加载页面。如果想强制从服务器重新加载,可以像下面这样给 reload()传个 true:

1
2
location.reload(); // 重新加载,可能是从缓存加载
location.reload(true); // 重新加载,从服务器加载

脚本中位于 reload()调用之后的代码可能执行也可能不执行,这取决于网络延迟和系统资源等因 素。为此,最好把 reload()作为最后一行代码。

检测插件

除 IE10 及更低版本外的浏览器,都可以通 过 plugins 数组来确定。这个数组中的每一项都包含如下属性。

 name:插件名称。

 description:插件介绍。

 filename:插件的文件名。

 length:由当前插件处理的 MIME 类型数量。

history对象

history 对象表示当前窗口首次使用以来用户的导航历史记录。因为 history 是 window 的属性, 所以每个 window 都有自己的 history 对象。

导航

go()方法可以在用户历史记录中沿任何方向导航,可以前进也可以后退。这个方法只接收一个参数, 这个参数可以是一个整数,表示前进或后退多少步。负值表示在历史记录中后退(类似点击浏览器的“后 退”按钮),而正值表示在历史记录中前进(类似点击浏览器的“前进”按钮)。下面来看几个例子:

1
2
3
4
5
6
7
// 后退一页
history.go(-1);
// 前进一页
history.go(1);
// 前进两页
history.go(2);

DOM

节点层次

DOM Level 1 描述了名为 Node 的接口,这个接口是所有 DOM 节点类型都必须实现的。Node 接口 在 JavaScript中被实现为 Node 类型,在除 IE之外的所有浏览器中都可以直接访问这个类型。在 JavaScript 中,所有节点类型都继承 Node 类型,因此所有类型都共享相同的基本属性和方法。

每个节点都有 nodeType 属性,表示该节点的类型。节点类型由定义在 Node 类型上的 12 个数值 常量表示,节点类型可通过与这些常量比较来确定,比如:

1
2
3
4
5
6
7
8
if (someNode.nodeType == Node.ELEMENT_NODE){
alert("Node is an element.");
}
if (someNode.nodeType == 1){
value = someNode.nodeName; // 会显示元素的标签名
}
//在这个例子中,先检查了节点是不是元素。如果是,则将其 nodeName 的值赋给一个变量。
//对元素而言,nodeName 始终等于元素的标签名,而 nodeValue 则始终为 null。

Node类型

节点关系

每个节点都有一个 childNodes 属性,其中包含一个 NodeList 的实例。NodeList 是一个类数组对象,用于存储可以按位置存取的有序节点。注意,NodeList 并不是 Array 的实例,但可以使用中括 号访问它的值,而且它也有 length 属性。NodeList 对象独特的地方在于,它其实是一个对 DOM 结 构的查询,因此 DOM 结构的变化会自动地在 NodeList 中反映出来。我们通常说 NodeList 是实时的 活动对象,而不是第一次访问时所获得内容的快照(后面的querySelectorAll()方法返回的NodeLis是快照,而不是实时的)。

1
2
3
4
5
//以下两种方法都是获取节点的方法
//即既可以用item()获得,也可以用中括号
let firstChild = someNode.childNodes[0];
let secondChild = someNode.childNodes.item(1);
let count = someNode.childNodes.length;

使用 Array.prototype. slice()可以像前面介绍 arguments 时一样把 NodeList 对象转换为数组。

1
let arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);

当然,使用 ES6 的 Array.from()静态方法,可以替换这种笨拙的方式:

1
let arrayOfNodes = Array.from(someNode.childNodes);

每个节点都有一个 parentNode 属性,指向其 DOM 树中的父元素。childNodes 中的所有节点都 有同一个父元素,因此它们的 parentNode 属性都指向同一个节点。此外,childNodes 列表中的每个 节点都是同一列表中其他节点的同胞节点。而使用 previousSibling 和 nextSibling 可以在这个列 表的节点间导航。这个列表中第一个节点的 previousSibling 属性是 null,最后一个节点的 nextSibling 属性也是 null,如下所示:

1
2
3
4
5
if (someNode.nextSibling === null){
alert("Last node in the parent's childNodes list.");
} else if (someNode.previousSibling === null){
alert("First node in the parent's childNodes list.");
}

父节点和它的第一个及最后一个子节点也有专门属性:firstChildlastChild 分别指向 childNodes 中的第一个和最后一个子节点。someNode.firstChild 的值始终等于 someNode. childNodes[0],而 someNode.lastChild 的值始终等于 someNode.childNodes[someNode. childNodes.length-1]。

操纵节点

  • appendChild()

appendChild(),用于在 childNodes 列表末尾添加节点。如果把文档中已经存在的节点传给 appendChild(),则这个节点会从之前的位置被转移到新位置。 即使 DOM 树通过各种关系指针维系,一个节点也不会在文档中同时出现在两个或更多个地方。

  • insertBefore()

如果想把节点放到 childNodes 中的特定位置而不是末尾,则可以使用 insertBefore()方法。 这个方法接收两个参数:要插入的节点和参照节点。调用这个方法后,要插入的节点会变成参照节点的 前一个同胞节点,并被返回。如果参照节点是 null,则 insertBefore()与 appendChild()效果相 同.

  • replaceChild()

replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档 树中完全移除,要插入的节点会取而代之。

  • removeChild()

要移除节点而不是替换节点,可以使用 removeChild()方法。这个方法接收一个参数,即要移除 的节点。被移除的节点会被返回.

以上四个方法都是通过父节点操纵其子元素.

其他方法

所有节点类型还共享了两个方法。**第一个是 cloneNode()**,会返回与调用它的节点一模一样的节 点。cloneNode()方法接收一个布尔值参数,表示是否深复制。在传入 true 参数时,会进行深复制, 即复制节点及其整个子 DOM 树。如果传入 false,则只会复制调用该方法的节点。复制返回的节点属 于文档所有,但尚未指定父节点,所以可称为孤儿节点(orphan)。

cloneNode()方法不会复制添加到 DOM 节点的 JavaScript 属性,比如事件处理程 序。这个方法只复制 HTML 属性,以及可选地复制子节点。除此之外则一概不会复制。

normalize()。这个方法唯一的任务就是处理文档子树中的文本节点。由于解析器实现的差异或 DOM 操作等原因,可能会出现并不包含文本的文本节点,或者文本节点之间互为同胞关系。在节点上调用 normalize()方法会检测这个节点的所有后代,从中搜索上述两种 情形。如果发现空文本节点,则将其删除;如果两个同胞节点是相邻的,则将其合并为一个文本节点。

Document 类型

特点

1
2
3
4
5
6
7
 nodeType 等于 9;
 nodeName 值为"#document";
 nodeValue 值为 null;
 parentNode 值为 null;
 ownerDocument 值为 null;
 子节点可以是 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction
或 Comment 类型。

Document 类型是 JavaScript 中表示文档节点的类型。在浏览器中,文档对象 document 是 HTMLDocument 的实例(HTMLDocument 继承 Document),表示整个 HTML 页面。document 是 window 对象的属性,因此是一个全局对象。

提供了两个访问子节点的快捷方式。第一个是 documentElement 属 性,始终指向 HTML 页面中的元素。虽然 document.childNodes 中始终有元素,但 使用 documentElement 属性可以更快更直接地访问该元素。

1
2
3
4
<html>
<body>
</body>
</html>
1
2
3
let html = document.documentElement; // 取得对<html>的引用
alert(html === document.childNodes[0]); // true
alert(html === document.firstChild); // true

作为 HTMLDocument 的实例,document 对象还有一个 body 属性,直接指向body元素。因为 这个元素是开发者使用最多的元素,所以 JavaScript 代码中经常可以看到 document.body,比如:

1
2
let body = document.body; // 取得对<body>的引用
let doctype = document.doctype; // 取得对<!doctype>的引用

文档信息

1
2
3
4
5
6
7
8
9
10
11
// 读取文档标题
let originalTitle = document.title;
// 修改文档标题
document.title = "New page title";

// 取得完整的 URL,URL 包含当前页面的完整 URL(地址栏中的 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 属性是可以设置的。出于安全考虑,给 domain 属性设置的值是有限制的。如果 URL包含子域名如 p2p.wrox.com,则可以将 domain 设置为”wrox.com”(URL包含“www” 时也一样,比如 www.wrox.com)。不能给这个属性设置 URL 中不包含的值,比如:

1
2
3
// 页面来自 p2p.wrox.com
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出错!

当页面中包含来自某个不同子域的窗格()或内嵌窗格(<iframe)时,设置
document.domain 是有用的。因为跨源通信存在安全隐患,所以不同子域的页面间无法通过 JavaScript通信。此时,在每个页面上把 document.domain 设置为相同的值,这些页面就可以访问对方的 JavaScript对象了。比如,一个加载自 www.wrox.com 的页面中包含一个内嵌窗格,其中的页面加载自p2p.wrox.com。这两个页面的 document.domain 包含不同的字符串,内部和外部页面相互之间不能访问对方的 JavaScript 对象。如果每个页面都把 document.domain 设置为wrox.com,那这两个页面之间就可以通信了。
浏览器对 domain 属性还有一个限制,即这个属性一旦放松就不能再收紧。比如,把
document.domain 设置为”wrox.com”之后,就不能再将其设置回”p2p.wrox.com”,后者会导致错
误,比如:

1
2
3
// 页面来自 p2p.wrox.com
document.domain = "wrox.com"; // 放松,成功
document.domain = "p2p.wrox.com"; // 收紧,错误!

定位元素

**getElementById()**和 **getElementsByTagName()**就是 Document 类型提供的两个方法。

getElementsByTagName()返回包含零个或多个元素的 NodeList。在 HTML 文档中,这个方法返回一个 HTMLCollection 对象。考虑到二者都是“实时”列表,HTMLCollection 与 NodeList 是很相似的。HTMLCollection 对象还有一个额外的方法 namedItem(),可通过标签的 name 属性取得某一项 的引用。例如,假设页面中包含如下的元素:

1
<img src="myimage.gif" name="myImage"> 

那么也可以像这样从 images 中取得对这个元素的引用:

1
2
3
let myImage = images.namedItem("myImage"); 
//也可以使用中括号直接获取
let myImage = images["myImage"];

对 HTMLCollection 对象而言,中括号既可以接收数值索引,也可以接收字符串索引。而在后台, 数值索引会调用 item(),字符串索引会调用 namedItem()。

HTMLDocument 类型上定义的获取元素的第三个方法是 **getElementsByName()**。getElementsByName()方法也返回 HTMLCollection。

Element 类型

特点

1
2
3
4
5
6
 nodeType 等于 1;
 nodeName 值为元素的标签名;
 nodeValue 值为 null;
 parentNode 值为 Document 或 Element 对象;
 子节点可以是 Element、Text、Comment、ProcessingInstruction、CDATASection、
EntityReference 类型。

所有这些都可以用来获取对应的属性值,也可以用来修改相应的值。比如有下面的 HTML 元素:

1
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div> 
1
2
3
4
5
6
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()。

1
2
3
4
5
6
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"

这种获取属性的方法和前面的不一样,重点为class,前面为className,这里为class,即元素中定义的什么属性名就是什么

创建元素

可以使用 document.createElement()方法创建新元素。这个方法接收一个参数,即要创建元素 的标签名。

1
let div = document.createElement("div"); 

可以使用 appendChild()、insertBefore()或 replaceChild()。 比如,以下代码会把刚才创建的元素添加到文档的元素中:

1
document.body.appendChild(div);

DOM编程

动态脚本

动态脚本就是在页面初始加载时不存在,之后又通过 DOM 包含的脚本。与对应的 HTML 元素一样,有两种方式通过

Prev
2021-07-16 09:00:00
Next