JavaScript 页面生命周期主要事件
- DOMContentLoaded
- load
- beforeunload/unload
一、DOMContentLoaded
DOMContentLoaded事件发生在document对象上,需用addEventListener来捕获它,此时浏览器已完全加载 HTML,并构建了 DOM 树。
document.addEventListener("DOMContentLoaded", ready)
1.1 DOMContentLoaded 和脚本
当浏览器处理一个 HTML 文档,并在文档中遇到 <script> 标签时,就会在继续构建 DOM 之前运行它。这是一种防范措施,因为脚本可能想要修改 DOM,甚至对其执行 document.write 操作,所以 DOMContentLoaded 必须等待脚本执行结束。
不会阻塞 DOMContentLoaded 的脚本:
具有 async 特性(attribute)的脚本不会阻塞 DOMContentLoaded,稍后 我们会讲到。
使用 document.createElement('script') 动态生成并添加到网页的脚本也不会阻塞 DOMContentLoaded。
1.2 DOMContentLoaded 和样式
外部样式表不会影响 DOM,因此 DOMContentLoaded 不会等待它们。但这里有一个陷阱。如果在样式后面有一个脚本,那么该脚本必须等待样式表加载完成:原因是,脚本可能想要获取元素的坐标和其他与样式相关的属性
1.3 浏览器内建的自动填充
Firefox,Chrome 和 Opera 都会在 DOMContentLoaded 中自动填充表单
如果 DOMContentLoaded 被需要加载很长时间的脚本延迟触发,那么自动填充也会等待
二、load
当整个页面,包括样式、图片和其他资源被加载完成时,会触发 window 对象上的 load 事件。可以通过 onload 属性获取此事件
三、readyState
document.readyState 是文档的当前状态,可以在 readystatechange 事件中跟踪状态更改
- loading —— 文档正在被加载。
- interactive —— 文档已被解析完成,与 DOMContentLoaded 几乎同时发生
- complete —— 文档和资源均已加载完成,与 window.onload 几乎同时发生,但是在 window.onload 之前发生。
JavaScript脚本
当浏览器加载 HTML 时遇到 <script>...</script> 标签,浏览器就不能继续构建 DOM。它必须立刻执行此脚本。对于外部脚本 <script src="..."></script> 也是一样的:浏览器必须等脚本下载完,并执行结束,之后才能继续处理剩余的页面
--- 有什么问题?
1.脚本不能访问到位于它们下面的 DOM 元素
2.脚本会阻塞页面
--- 有什么解决办法?
1.把脚本放在页面底部【浏览器只有在下载了完整的HTML文档之后才会开始下载脚本。对于长的 HTML文档来说,这样可能会造成明显的延迟】
2.defer,告诉浏览器不要等待脚本。浏览器将继续处理 HTML,构建 DOM。脚本会“在后台”下载,然后等 DOM 构建完成后,脚本才会执行【适用于外部脚本】
具有 defer 特性的脚本不会阻塞页面
具有 defer 特性的脚本总是要等到 DOM 解析完毕,但在 DOMContentLoaded 事件之前执行
具有 defer 特性的脚本保持其相对顺序,就像常规脚本一样
3.async【脚本是完全独立的】
async 脚本会在后台加载,并在加载就绪时运行
4.动态脚本
let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script); // (*)
当脚本被附加到文档 (*) 时,脚本就会立即开始加载。
默认情况下,动态脚本的行为是“异步”的。
function loadScript(src) {
let script = document.createElement('script');
script.src = src;
script.async = false;
document.body.append(script);
}
// long.js 先执行,因为代码中设置了 async=false
loadScript("/article/script-async-defer/long.js");
loadScript("/article/script-async-defer/small.js");
JavaScript资源加载 onload,onerror
浏览器允许我们跟踪外部资源的加载 —— 脚本,iframe,图片等
跨源策略
一个源(域/端口/协议三者)无法获取另一个源(origin)的内容
window.error无法捕获其他源的脚本的错误信息
--- 如何跨源?
有三个级别的跨源访问
1.无 crossorigin 特性 —— 禁止访问
2.crossorigin="anonymous" —— 如果服务器的响应带有包含 * 或我们的源(origin)的 header Access-Control-Allow-Origin,则允许访问。浏览器不会将授权信息和 cookie 发送到远程服务器。
3.crossorigin="use-credentials" —— 如果服务器发送回带有我们的源的 header Access-Control-Allow-Origin 和 Access-Control-Allow-Credentials: true,则允许访问。浏览器会将授权信息和 cookie 发送到远程服务器。
JavaScript跨窗口通信
“同源(Same Origin)”策略限制了窗口(window)和 frame 之间的相互访问
一个 <iframe> 标签承载了一个单独的嵌入的窗口,它具有自己的 document 和 window
iframe.contentWindow 来获取 <iframe> 中的 window
iframe.contentDocument 来获取 <iframe> 中的 document,是 iframe.contentWindow.document 的简写形式
如果尝试对来自另一个源的 <iframe> 进行读取和写入
1.可以通过 iframe.contentWindow 获取对内部 window 的引用
2.可以对 location 进行写入
一个 iframe 内可能嵌套了其他的 iframe。相应的 window 对象会形成一个层次结构(hierarchy)。可以通过以下方式获取:
window.frames —— “子”窗口的集合(用于嵌套的 iframe)。
window.parent —— 对“父”(外部)窗口的引用。
window.top —— 对最顶级父窗口的引用。
跨窗口通信
postMessage 接口允许窗口之间相互通信,无论它们来自什么源
想要发送消息的窗口需要调用接收窗口的 postMessage 方法。
win.postMessage(data, targetOrigin)
参数:
data
要发送的数据。可以是任何对象,IE 浏览器只支持字符串,因此我们需要对复杂的对象调用 JSON.stringify 方法进行处理,以支持该浏览器。
targetOrigin
指定目标窗口的源,以便只有来自给定的源的窗口才能获得该消息。
<iframe src="http://example.com" rel="external nofollow" rel="external nofollow" name="example">
<script>
let win = window.frames.example;
win.postMessage("message", "http://example.com");
</script>
onmessage
为了接收消息,目标窗口应该在 message 事件上有一个处理程序。当 postMessage 被调用时触发该事件(并且 targetOrigin 检查成功)
event 对象具有特殊属性:
data
从 postMessage 传递来的数据。
origin
发送方的源,例如 http://javascript.info。
source
对发送方窗口的引用。如果我们想,我们可以立即 source.postMessage(...) 回去。
window.addEventListener("message", function(event) {
if (event.origin != 'http://javascript.info') {
// 来自未知的源的内容,我们忽略它
return;
}
alert( "received: " + event.data );
// 可以使用 event.source.postMessage(...) 向回发送消息
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。