12 BOM
BOM-window
浏览器对象模型(BOM,Browser Object Model)
BOM(浏览器对象模型)主要用于让脚本与浏览器进行交互,而不是直接操作页面内容(那是 DOM 的职责)。
window
window 对象,表示浏览器的实例
通过 var 声明的所有全局变量和函 数都会变成 window 对象的属性和方法。
使用 moveTo()和 moveBy()方法移动窗口
innerWidth、innerHeight、outerWidth 和 outerHeight
resizeTo()和 resizeBy()方法
scroll()、scrollTo()和 scrollBy() 视口
window.open()方法可以用于导航到指定 URL,也可以用于打开新浏览器窗口
window.open("http://www.wrox.com/",
"wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");
窗口不会跟踪记录自己打开的新窗口, 1 因此开发者需要自己记录。
此外,浏览器会在用户操作下才允许创建弹窗。在网页加载过程中调用 window.open()没有效果
定时器 setTimeout
setTimeout()方法
// 设置超时任务
let timeoutId = setTimeout(() => alert("Hello world!"), 1000);
// 取消超时任务
clearTimeout(timeoutId);
setInterval()
let num = 0, intervalId = null;
let max = 10;
let incrementNumber = function() {
num++;
// 如果达到最大值,则取消所有未执行的任务
if (num == max) {
clearInterval(intervalId);
alert("Done"); }
}
intervalId = setInterval(incrementNumber, 500);
对话 或者说交互
使用 alert()、confirm()和 prompt()方法
find()和 print()
location
location 是最有用的 BOM 对象之一,提供了当前窗口中加载文档的信息,以及通常的导航功能
location 可以把 url 拆开
改 url:3 个
修改 location 对象修改浏览器的地址。首先,最常见的是使用 assign()方法并传入一 个 URL
如果不希望增加历史记录,可以使用 replace() 方法。这个方法接 收一个 URL 参数,但重新加载后不会增加历史记录。
最后一个修改地址的方法是 reload()
navigator
返回关于浏览器的具体信息 版本什么的 检测插件什么的
使用 registerProtocolHandler()方法将 Web 应用程注册为像桌面软件一样的默认应用程
screen
纯粹是客户端能力信息 只读多
history
导航历史记录
// 后退一页 history.go(-1);
// 前进一页 history.go(1);
// 前进两页 history.go(2);
hashchange 事件
而状态管理 API 则可以让开发者改变浏览器 URL 而不会加载新页面。为此,可以使用 history.pushState()方 法。
使用 HTML5 状态管理时,要确保通过 pushState()创建的每个“假”URL 背后 都对应着服务器上一个真实的物理 URL。否则,单击“刷新”按钮会导致 404 错误。所有 单页应用程序(SPA,Single Page Application)框架都必须通过服务器或客户端的某些配 置解决这个问题。
13 客户端检测
检查客户端是否拥有类似 window. Promise 之类的能力
能力检测
用户代理检测
不太可靠
软硬件检测
- 浏览器也提供了一些软件和硬件相关的信息。这些信息通过 screen 和 navigator 对象暴露出来。 利用这些 API,可以获取关于操作系统、浏览器、硬件、设备位置、电池状态等方面的准确信息。
14 DOM
文档对象模型(DOM,Document Object Model)是 HTML 和 XML 文档的编程接口。
DOM 中总共有 12 种节点类型,这些 类型都继承一种基本类型。
Node 类型
nodeType 判断具体类型
开发者最常用到的是元素节点和文本节点。
Document 类型
JavaScript 中表示文档节点的类型 表示整个 HTML 页面
document 是 window 对象的属性,因此是一个全局对象。
let html = document.documentElement; // 取得对<html>的引用
alert(html === document.childNodes[0]); // true
alert(html === document.firstChild); // true
11
接下来要介绍的 3 个属性是 URL、domain 和 referrer。其中,URL 包含当前页面的完整 URL(地 址栏中的 URL),domain 包含页面的域名,而 referrer 包含链接到当前页面的那个页面的 URL。如 果当前页面没有来源,则 referrer 属性包含空字符串。所有这些信息都可以在请求的 HTTP 头部信息 中获取,只是在 JavaScript 中通过这几个属性暴露出来而已,如下面的例子所示:
// 取得完整的URL
let url = document.URL;
// 取得域名
let domain = document.domain;
// 取得来源
let referrer = document.referrer;
在每个页面上把 document.domain 设置为相同的值,这些页面就可以访问对方的 JavaScript 对象了。比如,一个加载自 www.wrox.com 的页面中包含一个内嵌窗格,其中的页面加载自 p2p.wrox.com。这两个页面的 document.domain 包含不同的字符串,内部和外部页面相互之间不能 访问对方的 JavaScript 对象。如果每个页面都把 document.domain 设置为 wrox.com,那这两个页面 之间就可以通信了。
定位元素
使用 DOM 最常见的情形可能就是获取某个或某组元素的引用,然后对它们执行某些操作。 document 对象上暴露了一些方法,可以实现这些操作。getElementById()
和 getElementsByTagName()
就是 Document 类型提供的两个方法。getElementsByName()
<fieldset>
<legend>Which color do you prefer?</legend>
<ul>
<li>
<input type="radio" value="red" name="color" id="colorRed">
<label for="colorRed">Red</label>
</li> <li>
<input type="radio" value="green" name="color" id="colorGreen">
<label for="colorGreen">Green</label>
</li> </ul>
</fieldset>
let radios = document.getElementsByName("color");
写入 html
write()、
writeln()、open()和 close()
write()和 writeln()方法经常用于动态包含外部资源,如 JavaScript 文件。
加载后重写
<html>
<head>
<title>document.write() Example</title>
</head>
<body>
<p>This is some content that you won't get to see because it will be
overwritten.</p>
<script type="text/javascript">
window.onload = function(){
document.write("Hello world!");
}; </script>
</body>
</html>
Element 类型
除了 Document 类型,Element 类型就是 Web 开发中最常用的类型了
Element 表示 XML 或 HTML 元素,对外暴露出访问元素标签名、子节点和属性的能力。
if (element.tagName.toLowerCase() == "div"){ // 推荐,适用于所有文档 // 做点什么
}
getAttribute()方法也能取得不是 HTML 语言正式属性的自定义属性的值。比如下面的元素:
<div id="myDiv" my_special_attribute="hello!"></div>
通过 DOM 对象访问的属性中有两个返回的值跟使用 getAttribute()取得的值不一样。首先是 style 属性,第二个属性其实是一类,即事件处理程(或者事件属性)
开发者在进行 DOM 编程时通常会放弃使用 getAttribute()而只使用对象属性。 getAttribute()主要用于取得自定义属性的值。
创建元素
document.createElement()
for (let i = 0, len = element.childNodes.length; i < len; ++i) {
if (element.childNodes[i].nodeType == 1) {
// 执行某个操作 }
} 并且只在 nodeType 等于 1(即 Element 节点)时执行某个 操作。
let ul = document.getElementById("myList");
let items = ul.getElementsByTagName("li");
Text 类型
操作 xxxData(text)
比如 appendData (text)
获得
let textNode = div.firstChild; // 或 div.childNodes[0] 取得文本节点的引用后,可以像这样来修改它:
div.firstChild.nodeValue = "Some other message";
创建
let textNode = document.createTextNode("<strong>Hello</strong> world!");
let element = document.createElement("div");
element.className = "test";
document.body.appendChild(element);
Comment 类型 注释
DOM 编程
动态脚本
动态脚本就是在页面初始加载时不存在,之后又通过 DOM 包含的脚本
两种方式添加
引入外部文件和直接插入源代码。
function loadScript(url) {
let script = document.createElement("script");
script.src = url;
document.body.appendChild(script);
}
loadScript("client.js");
js 可以被直接插入 DOM 加载
动态样式
动态样式也是页面初始加载时并不存在,而是在之后才添加 到页面中的。
function loadStyles(url){
let link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
let head = document.getElementsByTagName("head")[0];
head.appendChild(link);
}
loadStyles("styles.css");
操作表格
// 创建表格
let table = document.createElement("table"); table.border = 1;
table.width = "100%";
// 创建表体
let tbody = document.createElement("tbody"); table.appendChild(tbody);
8 // 把表格添加到文档主体 9
14.2 DOM 编程 431
// 创建第一行
tbody.insertRow(0); 6 tbody.rows[0].insertCell(0); tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1")); tbody.rows[0].insertCell(1); tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1"));
// 创建第二行
tbody.insertRow(1);
tbody.rows[1].insertCell(0); tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2")); tbody.rows[1].insertCell(1); tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2"));
7
document.body.appendChild(table);
使用 NodeList
NodeList 就是基于 DOM 文档的实时查询
任何时候要迭代 NodeList,最好再初始化一个变量保存当时查询时的长度,然后用循环变量与这 个变量进行比较
let divs = document.getElementsByTagName("div");
for (let i = 0, len = divs.length; i < len; ++i) {
let div = document.createElement("div");
document.body.appendChild(div);
}
MutationObserver接口 对变化事件监控
- MutationObserver 接口是出于性能考虑而设计的,其核心是异步回调与记录队列模型。
可以在 DOM 被修改时异步执行回调。
使 用 MutationObserver 可以观察整个文档、DOM 树的一部分,或某个元素。此外还可以观察元素属性、子节点、文本,或者前三者任意组合的变化。
1. 动态响应 DOM 变化
-
意义:
当页面中的元素被添加、删除或修改时(例如用户操作、AJAX 更新、动画效果等),MutationObserver
能够捕获这些变化,使你有机会立即执行相应的逻辑,比如重新计算布局或更新页面内容。 -
举例:
比如在一个留言板应用中,当新留言通过 AJAX 动态添加到页面后,你可能需要自动滚动到最新留言或者高亮显示它。使用MutationObserver
可以检测到留言列表的变化,从而触发这些行为。2. 数据绑定
-
意义:
数据绑定是指数据的变化能够自动反映到页面上,或者页面的变化能够更新数据。虽然很多现代前端框架(如 Vue、React、Angular)都有内置的数据绑定机制,但在原生 JavaScript 中,如果你希望实现“视图与数据”的同步,监听 DOM 的变化可以是一种实现手段。 -
举例:
当你通过脚本更新一个数据模型后,页面上某个部分可能需要更新以反映最新数据。如果直接修改 DOM 的方式不够自动化,使用MutationObserver
监控相关 DOM 节点的变化,可以帮助你检测到数据是否被正确渲染,或者当用户直接操作 DOM 时(比如拖拽排序)更新数据模型。
3. 组件初始化
-
意义:
在动态加载或懒加载组件时(例如通过 JavaScript 动态创建元素),这些新加入的组件往往需要进行初始化(如绑定事件、启动插件、执行动画等)。
通过MutationObserver
,你可以监听某个容器内是否有新的组件插入,从而自动运行初始化代码,而不需要手动调用初始化函数。 -
举例:
假设你有一个页面区域会动态添加“卡片”组件,每个卡片需要绑定点击事件或展示动画。利用MutationObserver
监听该区域,一旦检测到新的卡片被插入,就立即调用初始化逻辑为它绑定事件或设置样式。
4. 懒加载
-
意义:
懒加载通常指在用户需要时(如滚动到视口内)再加载图片或其他资源,以节省初始加载时间和带宽。
虽然懒加载主要依赖于视口检测,但有时需要监听 DOM 中是否插入了新的需要懒加载的元素,再判断它们是否进入视口进行加载。 -
举例:
如果页面通过 AJAX 动态添加图片,利用MutationObserver
可以检测到图片元素的添加,然后结合视口检测(例如IntersectionObserver
),实现当图片滚动到用户可见区域时才进行资源加载。15 DOM 扩展
Selectors API
根据 CSS 选择符的模式匹配 DOM 元素
querySelector()
querySelectorAll() 返回 所有匹配的节点 快照
元素遍历
Element Traversal 规范
简单来说给你接口拿到子元素什么的
HTML5 classList
- getElementsByClassName()
- div.classList remove add contains toggle
焦点管理
第一个方法可以用来查询文档,确定哪个元素拥有焦点,第二个方法可以查询文档是否获得了焦点, 而这对于保证 Web 应用程的无障碍使用是非常重要的。
document.activeElement
其次是 document.hasFocus()方法
htmlDocument 扩展
document.readState
自定义数据属性
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
innerHTML
使用相关库转义后插入
scrollIntoView()
16 DOM2 和 DOM3
xml 命名空间
对于这样的文档,如果调用某个方法与节点交互,就会出现一个问题。比如,创建了一个新元素, 那这个元素属于哪个命名空间?查询特定标签名时,结果中应该包含哪个命名空间下的元素?DOM2 Core 为解决这些问题,给大部分 DOM1 方法提供了特定于命名空间的版本。
样式
`HTML 中的样式有 3 种定义方式:外部样式表(通过元素)、文档样式表(使用