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 设置包括标签在内的文本。
1<iframe height="300" style="width: 100%;" scrolling="no" title="innerHTML、innerText 和 outerHTML、outerText 的区别" src="https://codepen.io/powerfulyang/embed/xxjMwKV?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
2 See the Pen <a href="https://codepen.io/powerfulyang/pen/xxjMwKV">
3 innerHTML、innerText 和 outerHTML、outerText 的区别</a> by powerfulyang (<a href="https://codepen.io/powerfulyang">@powerfulyang</a>)
4 on <a href="https://codepen.io">CodePen</a>.
5</iframe>
自动重复
如果按下一个键足够长的时间,它就会开始“自动重复”: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.
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
肯定在下面的这些脚本执行结束之后发生:
1<script>
2 document.addEventListener("DOMContentLoaded", () => {
3 alert("DOM ready!");
4 });
5</script>
6
7<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
8
9<script>
10 alert("Library loaded, inline script executed");
11</script>
在上面这个例子中,我们首先会看到 “Library loaded…”,然后才会看到 “DOM ready!”(所有脚本都已经执行结束)。
此规则有两个例外:
- 具有
async
特性(attribute)的脚本不会阻塞DOMContentLoaded
。 - 使用
document.createElement('script')
动态生成并添加到网页的脚本也不会阻塞DOMContentLoaded
。
DOMContentLoaded 和样式
外部样式表不会影响 DOM,因此 DOMContentLoaded 不会等待它们。
但这里有一个陷阱。如果在样式后面有一个脚本,那么该脚本必须等待样式表加载完成:
1<link type="text/css" rel="stylesheet" href="style.css">
2<script>
3 // 在样式表加载完成之前,脚本都不会执行
4 alert(getComputedStyle(document.body).marginTop);
5</script>
原因是,脚本可能想要获取元素的坐标和其他与样式相关的属性,如上例所示。因此,它必须等待样式加载完成。
当 DOMContentLoaded 等待脚本时,它现在也在等待脚本前面的样式。
浏览器内建的自动填充
Firefox,Chrome 和 Opera 都会在 DOMContentLoaded
中自动填充表单。
例如,如果页面有一个带有登录名和密码的表单,并且浏览器记住了这些值,那么在 DOMContentLoaded
上,浏览器会尝试自动填充它们(如果得到了用户允许)。
因此,如果 DOMContentLoaded
被需要加载很长时间的脚本延迟触发,那么自动填充也会等待。你可能在某些网站上看到过(如果你使用浏览器自动填充)—— 登录名/密码字段不会立即自动填充,而是在页面被完全加载前会延迟填充。这实际上是 DOMContentLoaded
事件之前的延迟。
readyState
document.readyState
是文档的当前状态,可以在 readystatechange
事件中跟踪状态更改:
1addEventListener('readystatechange', (event) => {
2 // document.readyState
3});
loading
—— 文档正在被加载。interactive
—— 文档已被解析完成,与DOMContentLoaded
几乎同时发生,但是在DOMContentLoaded
之前发生。complete
—— 文档和资源均已加载完成,与window.onload
几乎同时发生,但是在window.onload
之前发生。
脚本:async,defer
defer
defer
特性告诉浏览器不要等待脚本。相反,浏览器将继续处理 HTML,构建 DOM。脚本会“在后台”下载,然后等 DOM 构建完成后,脚本才会执行。
这是与上面那个相同的示例,但是带有 defer
特性:
1<p>...content before script...</p>
2
3<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
4
5<!-- 立即可见 -->
6<p>...content after script...</p>
换句话说:
- 具有
defer
特性的脚本不会阻塞页面。 - 具有
defer
特性的脚本总是要等到 DOM 解析完毕,但在DOMContentLoaded
事件之前执行
DOMContentLoaded
事件处理程序等待具有 defer
特性的脚本执行完成。它仅在脚本下载且执行结束后才会被触发。
具有 defer
特性的脚本保持其相对顺序,就像常规脚本一样。
假设,我们有两个具有 defer
特性的脚本:long.js
在前,small.js
在后。
1<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
2<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。
这里有三个级别的跨源访问:
- 无
crossorigin
特性 —— 禁止访问。 crossorigin="anonymous"
—— 如果服务器的响应带有包含*
或我们的源(origin)的 headerAccess-Control-Allow-Origin
,则允许访问。浏览器不会将授权信息和 cookie 发送到远程服务器。crossorigin="use-credentials"
—— 如果服务器发送回带有我们的源的 headerAccess-Control-Allow-Origin
和Access-Control-Allow-Credentials: true
,则允许访问。浏览器会将授权信息和 cookie 发送到远程服务器
选择(Selection)和范围(Range)
这玩意知道就好,选中和光标位置设置