
文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载本篇技术指南以 Modern JavaScript Tutorial 的 UI 章节《Document and resource loading》为核心系统讲解 HTML 页面生命周期中的四个关键事件DOMContentLoaded、load、beforeunload与unload并辅以仓库内的可运行沙箱示例readystate.view、window-onbeforeunload.view验证真实事件时序。读完本文你将掌握何时初始化 DOM 交互、何时安全读取图片尺寸、如何在用户离开页面时上报统计以及如何通过document.readyState写出「无论脚本何时执行都能正确初始化」的健壮代码。页面生命周期三个关键事件概述一个 HTML 页面从开始加载到用户离开其生命周期由三个重要事件串联DOMContentLoaded—— 浏览器已完成 HTML 的解析、DOM 树构建完毕但外部资源如img图片、样式表可能尚未加载完成。load—— 不仅 HTML 加载完成所有外部资源图片、样式等也已全部就绪。beforeunload/unload—— 用户正在离开页面。每个事件都有明确的适用场景事件触发时机典型用途DOMContentLoadedDOM 就绪外部资源可能未加载查找 DOM 节点、初始化界面逻辑load外部资源全部加载完成读取已应用的样式、获取图片真实尺寸beforeunload用户即将离开检查未保存的更改并请求确认unload用户几乎已离开发起不阻塞的收尾操作如发送统计下面逐一深入这些事件的细节。DOMContentLoadedDOM 就绪的信号DOMContentLoaded事件发生在document对象上必须使用addEventListener捕获document.addEventListener(DOMContentLoaded, ready); // 不能写成 document.onDOMContentLoaded ...原因在于DOMContentLoaded没有对应的onDOMContentLoaded属性只能通过addEventListener注册。看一个典型示例——在DOMContentLoaded触发时虽然文档中的全部元素包括下方的img都已可见但图片本身可能尚未加载因此alert显示的尺寸为0x0script function ready() { alert(DOM is ready); // 图片尚未加载除非已被缓存所以尺寸是 0x0 alert(Image size: ${img.offsetWidth}x${img.offsetHeight}); } document.addEventListener(DOMContentLoaded, ready); /script img idimg srchttps://en.js.cx/clipart/train.gif?speed1cache0表面上看DOMContentLoaded很简单DOM 树就绪就触发。但其中有几个容易忽略的细节。DOMContentLoaded 与脚本的阻塞关系浏览器解析 HTML 时遇到script标签必须先执行脚本才能继续构建 DOM。这是必要的预防措施脚本可能想修改 DOM甚至调用document.write写入内容所以DOMContentLoaded必须等这些脚本执行完毕。因此DOMContentLoaded一定发生在所有普通脚本之后script document.addEventListener(DOMContentLoaded, () { alert(DOM ready!); }); /script script srchttps://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js/script script alert(Library loaded, inline script executed); /script上述示例的执行顺序是先看到 Library loaded...然后才是 DOM ready!所有脚本都执行完毕后。注意不阻塞 DOMContentLoaded 的脚本该规则存在两个例外带async属性的脚本——详见 Scripts: async, defer 一章它们不会阻塞DOMContentLoaded。通过document.createElement(script)动态创建并插入页面的脚本同样不会阻塞该事件。关于第 1 点2-ui/5-loading/02-script-async-defer/article.md 给出了完整的背景当浏览器遇到script src...时必须等它下载、执行完才能继续构建 DOM这会导致「顶部的大脚本阻塞页面」。defer属性让浏览器在后台加载脚本、等 DOM 构建完成后再执行async属性则让脚本加载完即执行、完全不等待 DOM。这些机制正是理解DOMContentLoaded何时触发的关键延伸。DOMContentLoaded 与样式的间接依赖外部样式表本身不影响 DOM所以DOMContentLoaded并不会等待样式表。但这里有一个陷阱如果样式表后面紧跟一个脚本那么这个脚本必须等样式表加载完成后才能执行link typetext/css relstylesheet hrefstyle.css script // 这个脚本要等样式表加载完成才执行 alert(getComputedStyle(document.body).marginTop); /script原因很实际脚本可能需要读取元素的坐标、以及其他依赖样式的属性如上面例子中的getComputedStyle自然必须等样式加载完毕。由于DOMContentLoaded要等待脚本而脚本又要等待其前面的样式DOMContentLoaded最终也间接等待了这些样式。浏览器内置表单自动填充Firefox、Chrome 和 Opera 会在DOMContentLoaded时自动填充表单。例如页面有登录/密码表单且浏览器记住了这些值那么在DOMContentLoaded时浏览器可能尝试自动填充如果用户批准。这意味着如果DOMContentLoaded被加载缓慢的脚本推迟自动填充也会随之推迟。你在某些网站上可能见过这种现象——使用浏览器自动填充时登录/密码字段不是立刻填好而是延迟到页面完全加载后才填充。这个延迟实际上就是DOMContentLoaded事件的延迟。window.onload等待全部资源window对象上的load事件在整个页面包括样式、图片及其他资源加载完成后触发可通过onload属性访问script window.onload function() { // 也可以写成 window.addEventListener(load, (event) { alert(Page loaded); // 此时图片已加载尺寸正确 alert(Image size: ${img.offsetWidth}x${img.offsetHeight}); }; /script img idimg srchttps://en.js.cx/clipart/train.gif?speed1cache0与DOMContentLoaded示例形成对比load会等待所有图片所以此时读取img.offsetWidth得到的是真实尺寸。window.onunload离开前的最后收尾当访问者离开页面时unload事件在window上触发。我们可以在其中做一些不涉及延迟的操作比如关闭关联的弹窗。一个重要的例外场景是发送统计数据。假设我们要收集页面使用数据鼠标点击、滚动、浏览区域等用户离开时自然想把数据保存到服务器。为此规范Beacon 规范见 2-ui/5-loading/01-onload-ondomcontentloaded/article.md 引用的 w3c 标准提供了专门的navigator.sendBeacon(url, data)方法。它会在后台发送数据不会延迟页面跳转浏览器虽然已离开页面但仍会执行sendBeacon请求。用法示例let analyticsData { /* object with gathered data */ }; window.addEventListener(unload, function() { navigator.sendBeacon(/analytics, JSON.stringify(analyticsData)); });使用sendBeacon需要注意请求以POST方式发送。不仅可以发送字符串还可以发送表单等其他格式详见 Fetch 一章但通常发送的是序列化后的对象。数据大小限制为 64KB。sendBeacon请求结束时浏览器很可能已经离开当前文档因此无法获取服务器响应统计接口的响应通常为空影响不大。对于需要在「页面离开后」继续进行的通用网络请求fetch方法还提供了keepalive标志。仓库中的 Fetch API 一章第 194–223 行对keepalive有完整说明正常文档卸载时所有关联的网络请求会被中止keepalive: true告诉浏览器在后台执行请求即使页面已经离开也能成功。keepalive请求同样受64KB body 限制且该限制作用于所有keepalive请求的总和——可以并行发起多个但 body 长度之和不能超过 64KB。文档已卸载时无法处理服务器响应。unload中无法取消跳转但可以用另一个事件——onbeforeunload。window.onbeforeunload离开前的确认当访问者发起离开页面的导航或尝试关闭窗口时beforeunload处理器会请求额外确认。如果取消事件浏览器可能会询问访问者是否确定离开。试试运行以下代码后刷新页面window.onbeforeunload function() { return false; };由于历史原因返回非空字符串同样被视为取消事件。过去浏览器会把它作为消息展示给用户但现代规范已不允许这样做window.onbeforeunload function() { return There are unsaved changes. Leave now?; };行为变更的原因是一些站长滥用该处理器展示误导性、烦人的消息。所以现在只有旧浏览器仍可能把字符串显示为消息除此之外无法自定义展示给用户的消息内容。警告beforeunload处理器中的event.preventDefault()不生效听起来可能很奇怪但大多数浏览器会忽略event.preventDefault()。也就是说下面这段代码可能不生效window.addEventListener(beforeunload, (event) { // 不生效所以这个处理器什么也不做 event.preventDefault(); });正确的做法是在这种处理器中设置event.returnValue为字符串才能获得与上面return方式类似的效果window.addEventListener(beforeunload, (event) { // 生效与从 window.onbeforeunload 中 return 效果相同 event.returnValue There are unsaved changes. Leave now?; });仓库中提供了可交互的验证沙箱 window-onbeforeunload.view/index.html页面包含一个「Set window.onbeforeunload」按钮用于动态设置window.onbeforeunload处理器以及一个指向外部站点的链接用于触发导航离开。点击按钮后点击该链接即可在浏览器中观察确认弹窗的触发行为。document.readyState查询当前加载状态如果在文档加载完成之后才设置DOMContentLoaded处理器它自然永远不会运行。但当不确定文档是否已就绪时我们希望函数「无论现在还是稍后」都能在 DOM 加载后执行。document.readyState属性告诉我们当前加载状态共 3 个可能值值含义loading文档正在加载interactive文档已完全解析读取complete文档已完全解析且所有资源如图片也已加载所以可以检查document.readyState仍在加载就注册DOMContentLoaded处理器否则立即执行代码function work() { /*...*/ } if (document.readyState loading) { // 仍在加载等待事件 document.addEventListener(DOMContentLoaded, work); } else { // DOM 就绪 work(); }还有readystatechange事件会在状态变化时触发可以像这样打印所有状态// 当前状态 console.log(document.readyState); // 打印状态变化 document.addEventListener(readystatechange, () console.log(document.readyState));readystatechange是追踪文档加载状态的另一种机制出现得较早如今已很少使用。完整事件流readystate 沙箱验证为了完整呈现事件的先后顺序仓库提供了 readystate.view 沙箱文档包含iframe、img并为所有相关事件注册了日志处理器对应文件为 readystate.view/iframe.html。核心代码如下script log(initial readyState: document.readyState); document.addEventListener(readystatechange, () log(readyState: document.readyState)); document.addEventListener(DOMContentLoaded, () log(DOMContentLoaded)); window.onload () log(window onload); /script iframe srciframe.html onloadlog(iframe onload)/iframe img srchttps://en.js.cx/clipart/train.gif idimg script img.onload () log(img onload); /script沙箱中使用performance.now()记录每个事件发生的毫秒时间见 readystate.view/index.html 中的log函数并填充大量隐藏文本来延长文档解析时间以便更清晰地拉开事件间隔。典型输出如下[1] initial readyState:loading[2] readyState:interactive[2] DOMContentLoaded[3] iframe onload[4] img onload[4] readyState:complete[4] window onload方括号中的数字表示事件发生的大致时间标有相同数字的事件在相近时刻发生相差几毫秒。由此可以得出两个关键结论document.readyState变为interactive的时刻紧挨在DOMContentLoaded之前这两者实际含义相同。document.readyState变为complete是在所有资源iframe和img加载完成之后这里可以看到它和img.onload最后一个资源、window.onload几乎同时发生。切换到complete状态与window.onload含义相同区别在于window.onload总是在其他所有load处理器之后运行。总结页面加载事件的完整速查页面加载事件一览DOMContentLoaded—— DOM 就绪时在document上触发此时可以对元素应用 JavaScript。script.../script或script src.../script会阻塞DOMContentLoaded浏览器要等它们执行完毕。图片等其他资源可能仍在加载中。load—— 页面及所有资源加载完成时在window上触发。很少使用因为通常没必要等那么久。beforeunload—— 用户想离开页面时在window上触发。如果取消事件浏览器会询问用户是否确定离开例如存在未保存的更改时。unload—— 用户最终离开时在window上触发处理器中只能做不涉及延迟、无需询问用户的简单操作因此很少使用可以通过navigator.sendBeacon发出网络请求。document.readyState—— 文档的当前状态可通过readystatechange事件追踪变化loading—— 文档正在加载。interactive—— 文档已解析完成大约与DOMContentLoaded同时发生但略早于它。complete—— 文档与资源均已加载大约与window.onload同时发生但略早于它。实践建议日常开发中优先在DOMContentLoaded中初始化交互逻辑查找节点、绑定事件、读取无需样式的属性需要读取图片尺寸、样式相关数据时再用load上报统计使用unloadnavigator.sendBeacon或fetch的keepalive: true提示用户保存未提交更改使用beforeunload并牢记event.returnValue而非preventDefault()。若脚本可能晚于文档加载执行用document.readyState判断后决定立即执行还是等待DOMContentLoaded即可写出时序无关的健壮初始化代码。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐Modern JavaScript Tutorial深入掌握 async/await 异步编程Modern JavaScript Tutorial深入掌握 async/await 异步编程 导读 async/await 是 JavaScript 中基于文档/教程前端掌控页面生命周期PageLifecycle.js掌控页面生命周期PageLifecycle.js 在网页开发的世界里了解页面的状态和其生命周期对于优化性能和用户体验至关重要。这就是为什么我们要向你推荐一个前端JavaScript状态机生命周期事件全面解析JavaScript状态机生命周期事件全面解析 引言为什么需要状态机生命周期事件 在现代前端开发中状态管理State Management是构建复杂应开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考