DOM 相关

UIEvent.detail

The UIEvent.detail read-only property, when non-zero, provides the current (or next, depending on the event) click count.
For click or dblclick events, UIEvent.detail is the current click count.
For mousedown or mouseup events, UIEvent.detail is 1 plus the current click count.
For all other UIEvent objects, UIEvent.detail is always zero.

event.button

event.button 的所有可能值如下:

鼠标按键状态event.button
左键 (主要按键)0
中键 (辅助按键)1
右键 (次要按键)2
X1 键 (后退按键)3
X2 键 (前进按键)4

非替换元素和替换元素

替换元素

  • 替换元素是浏览器根据其标签属性来判断显示具体的内容。
  • 比如:<input />, type="text" 时,这是一个文本输入框,type 换一个值,浏览器显示就不一样。
  • 常见的可替换元素,例如 <iframe><video><embed><img> 等,有些元素在特定情况下会被当作可替换元素处理,例如 <option><audio><canvas><object><applet><input> 等。

非替换元素

  • 非替换元素的内容不会超出 CSS 的模型范围,CSS 在渲染时会考虑非替换元素的内容。
  • HTML 的大多数元素是不可替换元素,即其内容直接表现给浏览器,例如 <div><p><h1>~<h6><table> 等。

innerHTML、innerText 和 outerHTML、outerText 的区别

  • innerHTML 设置或获取位于对象起始和结束标签内的 HTML
  • outerHTML 设置或获取对象及其内容的 HTML 形式
  • innerText 设置或获取位于对象起始和结束标签内的文本
  • outerText 设置(包括标签)或获取(不包括标签)对象的文本

innerText 和 outerText 在获取时是相同效果,但在设置时,innerText 仅设置标签内的文本,而outerText 设置包括标签在内的文本。



自动重复

如果按下一个键足够长的时间,它就会开始“自动重复”:keydown 会被一次又一次地触发,然后当按键被释放时,我们最终会得到 keyup。因此,有很多 keydown 却只有一个 keyup 是很正常的。
对于由自动重复触发的事件,event 对象的 event.repeat 属性被设置为 true

防止滚动

我们如何使某些东西变成不可滚动?
我们不能通过在 onscroll 监听器中使用 event.preventDefault() 来阻止滚动,因为它会在滚动发生 之后 才触发。
但是我们可以在导致滚动的事件上,例如在 pageUp 和 pageDown 的 keydown 事件上,使用 event.preventDefault() 来阻止滚动。
如果我们向这些事件中添加事件处理程序,并向其中添加 event.preventDefault(),那么滚动就不会开始。
启动滚动的方式有很多,使用 CSS 的 overflow 属性更加可靠。

移动端点击,触发click事件存在300ms延迟

在十几年前,当时的网页基本上都是为PC设备所设计的,没有什么移动端适配的概念,导致字体看起来非常小,阅读困难。
为了处理这种情况,苹果的工程师们想了各种应对方案,其中最为出名的,当属双击缩放(double tap to zoom)。通过双击,在放大比例和原始比例之间进行切换。
如果判断用户是点击还是双击呢?苹果的逻辑如下:
在用户点击完此处第一次后,如果300ms内没有在此处进行第二次点击,就认为是一个纯点击操作。
这就是300ms延迟的来源,浏览器通过300ms的时间间隔猜测你的行为意图,试图分辨你是想单击还是双击。
解决办法: 现在已经不会有这种事啦,无需 fastclick 用原生 click 就好
Chrome 32+ on Android with width=device-width in the viewport meta tag doesn't have a 300ms delay.

html
1
<meta name="viewport" content="width=device-width, initial-scale=1"/>

Same goes for Chrome on Android (all versions) with user-scalable=no in the viewport meta tag. But be aware that user-scalable=no also disables pinch zooming, which may be an accessibility concern.

For IE11+, you can use touch-action: manipulation; to disable double-tap-to-zoom on certain elements (like links and buttons). For IE10 use -ms-touch-action: manipulation.

DOMContentLoaded

DOMContentLoaded 和 脚本

当浏览器处理一个 HTML 文档,并在文档中遇到 <script> 标签时,就会在继续构建 DOM 之前运行它。这是一种防范措施,因为脚本可能想要修改 DOM,甚至对其执行 document.write 操作,所以 DOMContentLoaded 必须等待脚本执行结束。
因此,DOMContentLoaded 肯定在下面的这些脚本执行结束之后发生:

js
1
2
3
4
5
6
7
8
9
10
11
<script>
  document.addEventListener("DOMContentLoaded", () => {
    alert("DOM ready!");
  });
</script>

<script src="https://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!”(所有脚本都已经执行结束)。

此规则有两个例外:

  1. 具有 async 特性(attribute)的脚本不会阻塞 DOMContentLoaded
  2. 使用 document.createElement('script') 动态生成并添加到网页的脚本也不会阻塞 DOMContentLoaded

DOMContentLoaded 和样式

外部样式表不会影响 DOM,因此 DOMContentLoaded 不会等待它们。
但这里有一个陷阱。如果在样式后面有一个脚本,那么该脚本必须等待样式表加载完成:

js
1
2
3
4
5
<link type="text/css" rel="stylesheet" href="style.css">
<script>
  // 在样式表加载完成之前,脚本都不会执行
  alert(getComputedStyle(document.body).marginTop);
</script>

原因是,脚本可能想要获取元素的坐标和其他与样式相关的属性,如上例所示。因此,它必须等待样式加载完成。
当 DOMContentLoaded 等待脚本时,它现在也在等待脚本前面的样式。

浏览器内建的自动填充

Firefox,Chrome 和 Opera 都会在 DOMContentLoaded 中自动填充表单。
例如,如果页面有一个带有登录名和密码的表单,并且浏览器记住了这些值,那么在 DOMContentLoaded 上,浏览器会尝试自动填充它们(如果得到了用户允许)。
因此,如果 DOMContentLoaded 被需要加载很长时间的脚本延迟触发,那么自动填充也会等待。你可能在某些网站上看到过(如果你使用浏览器自动填充)—— 登录名/密码字段不会立即自动填充,而是在页面被完全加载前会延迟填充。这实际上是 DOMContentLoaded 事件之前的延迟。

readyState

document.readyState 是文档的当前状态,可以在 readystatechange 事件中跟踪状态更改:

js
1
2
3
addEventListener('readystatechange', (event) => {
  // document.readyState
});
  • loading —— 文档正在被加载。
  • interactive —— 文档已被解析完成,与 DOMContentLoaded 几乎同时发生,但是在 DOMContentLoaded 之前发生。
  • complete —— 文档和资源均已加载完成,与 window.onload 几乎同时发生,但是在 window.onload 之前发生。

脚本:async,defer

defer

defer 特性告诉浏览器不要等待脚本。相反,浏览器将继续处理 HTML,构建 DOM。脚本会“在后台”下载,然后等 DOM 构建完成后,脚本才会执行。
这是与上面那个相同的示例,但是带有 defer 特性:

html
1
2
3
4
5
6
<p>...content before script...</p>

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<!-- 立即可见 -->
<p>...content after script...</p>

换句话说:

  • 具有 defer 特性的脚本不会阻塞页面。
  • 具有 defer 特性的脚本总是要等到 DOM 解析完毕,但在 DOMContentLoaded 事件之前执行

DOMContentLoaded 事件处理程序等待具有 defer 特性的脚本执行完成。它仅在脚本下载且执行结束后才会被触发。
具有 defer 特性的脚本保持其相对顺序,就像常规脚本一样。
假设,我们有两个具有 defer 特性的脚本:long.js 在前,small.js 在后。

html
1
2
<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>

浏览器扫描页面寻找脚本,然后并行下载它们,以提高性能。因此,在上面的示例中,两个脚本是并行下载的。small.js 可能会先下载完成。
……但是,defer 特性除了告诉浏览器“不要阻塞页面”之外,还可以确保脚本执行的相对顺序。因此,即使 small.js 先加载完成,它也需要等到 long.js 执行结束才会被执行。
defer 特性仅适用于外部脚本
如果 <script> 脚本没有 src,则会忽略 defer 特性。

async

async 特性意味着脚本是完全独立的:

  • 浏览器不会因 async 脚本而阻塞(与 defer 类似)。
  • 其他脚本不会等待 async 脚本加载完成,同样,async 脚本也不会等待其他脚本。
  • DOMContentLoaded 和异步脚本不会彼此等待:
    • DOMContentLoaded 可能会发生在异步脚本之前(如果异步脚本在页面完成后才加载完成)
    • DOMContentLoaded 也可能发生在异步脚本之后(如果异步脚本很短,或者是从 HTTP 缓存中加载的)

async 特性仅适用于外部脚本

动态脚本

默认情况下,动态脚本的行为是“异步”的。
如果我们显式地设置了 script.async=false,则可以改变这个规则。然后脚本将按照脚本在文档中的顺序执行,就像 defer 那样。

跨源策略

如果我们使用的是来自其他域的脚本,并且该脚本中存在 error,那么我们无法获取 error 的详细信息。
要允许跨源访问,<script> 标签需要具有 crossorigin 特性(attribute),并且远程服务器必须提供特殊的 header。
这里有三个级别的跨源访问:

  1. crossorigin 特性 —— 禁止访问。
  2. crossorigin="anonymous" —— 如果服务器的响应带有包含 * 或我们的源(origin)的 header Access-Control-Allow-Origin,则允许访问。浏览器不会将授权信息和 cookie 发送到远程服务器。
  3. crossorigin="use-credentials" —— 如果服务器发送回带有我们的源的 header Access-Control-Allow-OriginAccess-Control-Allow-Credentials: true,则允许访问。浏览器会将授权信息和 cookie 发送到远程服务器

选择(Selection)和范围(Range)

这玩意知道就好,选中和光标位置设置