鼠标事件
此类事件不仅可能来自于“鼠标设备”,还可能来自于对此类操作进行了模拟以实现兼容性的其他设备,例如手机和平板电脑。
鼠标事件类型
我们已经见过了其中一些事件:
mousedown/mouseup
在元素上点击/释放鼠标按钮。
mouseover/mouseout
鼠标指针从一个元素上移入/移出。
mousemove
鼠标在元素上的每个移动都会触发此事件。
click
如果使用的是鼠标左键,则在同一个元素上的 mousedown
及 mouseup
相继触发后,触发该事件。
dblclick
在短时间内双击同一元素后触发。如今已经很少使用了。
contextmenu
在鼠标右键被按下时触发。还有其他打开上下文菜单的方式,例如使用特殊的键盘按键,在这种情况下它也会被触发,因此它并不完全是鼠标事件。
事件顺序
从上面的列表中我们可以看到,一个用户操作可能会触发多个事件。
在单个动作触发多个事件时,事件的顺序是固定的。
鼠标按钮
与点击相关的事件始终具有 button
属性,该属性允许获取确切的鼠标按钮。
通常我们不在 click
和 contextmenu
事件中使用这一属性,因为前者只在单击鼠标左键时触发,后者只在单击鼠标右键时触发。
不过,在 mousedown
和 mouseup
事件中则可能需要用到 event.button
,因为这两个事件在任何按键上都会触发,所以我们可以使用 button
属性来区分是左键单击还是右键单击。
event.button
的所有可能值如下
鼠标按键状态 | event.button |
---|---|
左键 (主要按键) | 0 |
中键 (辅助按键) | 1 |
右键 (次要按键) | 2 |
X1 键 (后退按键) | 3 |
X2 键 (前进按键) | 4 |
大多数鼠标设备只有左键和右键,对应的值就是 0 和 2 。触屏设备中的点按操作也会触发类似的事件。 |
|
另外,还有一个 event.buttons 属性,其中以整数的形式存储着当前所有按下的鼠标按键,每个按键一个比特位。在实际开发中,很少会用到这个属性,如果有需要的话,你可以在 MDN 中找到更多细节。 |
过时的
event.which
一些老代码可能会使用event.which
属性来获得按下的按键。这是一个古老的非标准的方式,具有以下可能值:
event.which == 1
—— 鼠标左键,event.which == 2
—— 鼠标中键,event.which == 3
—— 鼠标右键。
现在,event.which
已经被弃用了,不应再使用它。
组合键 shift alt ctrl meta
所有的鼠标事件都包含有关按下的组合键的信息。
事件属性:
shiftKey
:ShiftaltKey
:Alt(或对于 Mac 是 Opt)ctrlKey
:CtrlmetaKey
:对于 Mac 是 Cmd
如果在事件期间按下了相应的键,则它们为true
。
注意:在 Mac 上我们通常使用
Cmd
代替Ctrl
在 Windows 和 Linux 上有 Alt,Shift 和 Ctrl。在 Mac 上还有:Cmd,它对应于属性metaKey
。
在大多数情况下,当在 Windows/Linux 上使用 Ctrl 时,在 Mac 是使用 Cmd。
也就说:当 Windows 用户按下 Ctrl+Enter 或 Ctrl+A 时,Mac 用户会按下 Cmd+Enter 或 Cmd+A,以此类推。
因此,如果我们想支持 Ctrl+click,那么对于 Mac 应该使用 Cmd+click。对于 Mac 用户而言,这更舒适。
即使我们想强制 Mac 用户使用 Ctrl+click —— 这非常困难。问题是:在 MacOS 上左键单击和 Ctrl 一起使用会被解释为 右键单击,并且会生成contextmenu
事件,而不是像 Windows/Linux 中的click
事件。
因此,如果我们想让所有操作系统的用户都感到舒适,那么我们应该将ctrlKey
与metaKey
一起进行检查。
对于 JS 代码,这意味着我们应该检查if (event.ctrlKey || event.metaKey)
。
还有移动设备
键盘组合是工作流的一个补充。这样,如果访客使用键盘操作 —— 它们就会起作用。
但是,如果访客的设备没有键盘 —— 那么这里应该有另一种不使用键盘也能做到这一点的方式。
坐标 clientX/Y, pageX/Y
所有的鼠标事件都提供了两种形式的坐标:
- 相对于窗口的坐标:
clientX
和clientY
。 - 相对于文档的坐标:
pageX
和pageY
。
我们已经在 坐标 中解释过它们之间的区别。
简而言之,相对于文档的坐标pageX/Y
以文档的左上角为参照物,并且同一位置的坐标不随页面的滚动而改变。相对于窗口的坐标clientX/Y
以当前窗口的左上角为参照物,并且同一位置的坐标会随着页面的滚动而改变。
防止在鼠标按下时的选择
双击鼠标会有副作用,在某些界面中可能会出现干扰:它会选择文本。
如果按下鼠标左键,并在不松开的情况下移动鼠标,这也常常会造成不必要的选择。
有多种防止选择的方法,你可以在 选择(Selection)和范围(Range) 一章中详细阅读。
在这种情况下,最合理的方式是防止浏览器对 mousedown
进行操作。
防止复制
如果我们想禁用选择以保护我们页面的内容不被复制粘贴,那么我们可以使用另一个事件:oncopy
。
如果你试图在<div>
中复制一段文本,这是行不通的,因为默认行为oncopy
被阻止了。
当然,用户可以访问页面的 HTML 源码,并且可以从那里获取内容,但并不是每个人都知道如何做到这一点。
移动鼠标 mouseover/out mouseenter/leave
事件 mouseover/mouseout relatedTarget
当鼠标指针移到某个元素上时,mouseover
事件就会发生,而当鼠标离开该元素时,mouseout
事件就会发生。
这些事件很特别,因为它们具有 relatedTarget
属性。此属性是对 target
的补充。当鼠标从一个元素离开并去往另一个元素时,其中一个元素就变成了 target
,另一个就变成了 relatedTarget
。
对于 mouseover
:
event.target
—— 是鼠标移过的那个元素。event.relatedTarget
—— 是鼠标来自的那个元素(relatedTarget
→target
)。
mouseout
则与之相反:event.target
—— 是鼠标离开的元素。event.relatedTarget
—— 是鼠标移动到的,当前指针位置下的元素(target
→relatedTarget
)。
在下面这个示例中,每张脸及其功能都是单独的元素。当你移动鼠标时,你可以在文本区域中看到鼠标事件。
relatedTarget
可以为null
relatedTarget
属性可以为null
。
这是正常现象,仅仅是意味着鼠标不是来自另一个元素,而是来自窗口之外。或者它离开了窗口。
当我们在代码中使用event.relatedTarget
时,我们应该牢记这种可能性。如果我们访问event.relatedTarget.tagName
,那么就会出现错误。
跳过元素
当鼠标移动时,就会触发 mousemove
事件。但这并不意味着每个像素都会导致一个事件。
浏览器会一直检查鼠标的位置。如果发现了变化,就会触发事件。
这意味着,如果访问者非常快地移动鼠标,那么某些 DOM 元素就可能被跳过
这对性能很有好处,因为可能有很多中间元素。我们并不真的想要处理每一个移入和离开的过程。
另一方面,我们应该记住,鼠标指针并不会“访问”所有元素。它可以“跳过”一些元素。
特别是,鼠标指针可能会从窗口外跳到页面的中间。在这种情况下,relatedTarget
为 null
,因为它是从石头缝里蹦出来的(nowhere)
如果
mouseover
被触发了,则必须有mouseout
在鼠标快速移动的情况下,中间元素可能会被忽略,但是我们可以肯定一件事:如果鼠标指针“正式地”进入了一个元素(生成了mouseover
事件),那么一旦它离开,我们就会得到mouseout
。
当移动到一个子元素时 mouseout
mouseout
的一个重要功能 —— 当鼠标指针从元素移动到其后代时触发,例如在下面的这个 HTML 中,从 #parent
到 #child
如果我们在 #parent
上,然后将鼠标指针更深入地移入 #child
,在 #parent
上我们会得到 mouseout
!
这听起来很奇怪,但很容易解释。
根据浏览器的逻辑,鼠标指针随时可能位于单个元素上 —— 嵌套最多的那个元素(z-index 最大的那个)。
因此,如果它转到另一个元素(甚至是一个后代),那么它将离开前一个元素。
请注意事件处理的另一个重要的细节。
后代的 mouseover
事件会冒泡。因此,如果 #parent
具有 mouseover
处理程序,它将被触发
如果我们不检查处理程序中的 event.target
,那么似乎鼠标指针离开了 #parent
元素,然后立即回到了它上面。
但是事实并非如此!鼠标指针仍然位于父元素上,它只是更深入地移入了子元素。
如果离开父元素时有一些行为(action),例如一个动画在 parent.onmouseout
中运行,当鼠标指针深入 #parent
时,我们并不希望发生这种行为。
为了避免它,我们可以在处理程序中检查 relatedTarget
,如果鼠标指针仍在元素内,则忽略此类事件。
另外,我们可以使用其他事件:mouseenter
和 mouseleave
,它们没有此类问题,接下来我们就对其进行详细介绍。
事件 mouseenter mouseleave
事件 mouseenter/mouseleave
类似于 mouseover/mouseout
。它们在鼠标指针进入/离开元素时触发。
但是有两个重要的区别:
- 元素内部与后代之间的转换不会产生影响。
- 事件
mouseenter/mouseleave
不会冒泡。
这些事件非常简单。
当鼠标指针进入一个元素时 —— 会触发mouseenter
。而鼠标指针在元素或其后代中的确切位置无关紧要。
当鼠标指针离开该元素时,事件mouseleave
才会触发。
事件委托
事件 mouseenter/leave
非常简单且易用。但它们不会冒泡。因此,我们不能使用它们来进行事件委托。
假设我们要处理表格的单元格的鼠标进入/离开。并且这里有数百个单元格。
通常的解决方案是 —— 在 <table>
中设置处理程序,并在那里处理事件。但 mouseenter/leave
不会冒泡。因此,如果类似的事件发生在 <td>
上,那么只有 <td>
上的处理程序才能捕获到它。
<table>
上的 mouseenter/leave
的处理程序仅在鼠标指针进入/离开整个表格时才会触发。无法获取有关其内部移动的任何信息。
因此,让我们使用 mouseover/mouseout
。
鼠标拖放事件
拖放(Drag’n’Drop)是一个很赞的界面解决方案。取某件东西并将其拖放是执行许多东西的一种简单明了的方式,从复制和移动文档(如在文件管理器中)到订购(将物品放入购物车)。
在现代 HTML 标准中有一个 关于拖放的部分,其中包含了例如 dragstart
和 dragend
等特殊事件。
这些事件使我们能够支持特殊类型的拖放,例如处理从 OS 文件管理器中拖动文件,并将其拖放到浏览器窗口中。之后,JavaScript 便可以访问此类文件中的内容。
但是,原生的拖放事件也有其局限性。例如,我们无法阻止从特定区域的拖动。并且,我们无法将拖动变成“水平”或“竖直”的。还有很多其他使用它们无法完成的拖放任务。并且,移动设备对此类事件的支持非常有限。
因此,在这里我们将看到,如何使用鼠标事件来实现拖放。
拖放算法
基础的拖放算法如下所示:
- 在
mousedown
上 —— 根据需要准备要移动的元素(也许创建一个它的副本,向其中添加一个类或其他任何东西)。 - 然后在
mousemove
上,通过更改position:absolute
情况下的left/top
来移动它。 - 在
mouseup
上 —— 执行与完成的拖放相关的所有行为。
指针事件
指针事件(Pointer Events)是一种用于处理来自各种输入设备(例如鼠标、触控笔和触摸屏等)的输入信息的现代化解决方案。
历史
让我们先做一个简短的概览,以便你对指针事件及其在其它事件类型中所处位置有个粗略认识。
- 很早以前,只存在鼠标事件。
后来,触屏设备开始普及,尤其是手机和平板电脑。为了使现有的脚本仍能正常工作,它们生成(现在仍生成)鼠标事件。例如,轻触屏幕就会生成mousedown
事件。因此,触摸设备可以很好地与网页配合使用。
但是,触摸设备比鼠标具有更多的功能。例如,我们可以同时触控多点(多点触控)。然而,鼠标事件并没有相关属性来处理这种多点触控。 - 因此,引入了触摸事件,例如
touchstart
、touchend
和touchmove
,它们具有特定于触摸的属性(这里不再赘述这些特性,因为指针事件更加完善)。
不过这还是不够完美。因为很多其他输入设备(如触控笔)都有自己的特性。而且同时维护两份分别处理鼠标事件和触摸事件的代码,显得有些笨重了。 - 为了解决这些问题,人们引入了全新的规范「指针事件」。它为各种指针输入设备提供了一套统一的事件。
目前,各大主流浏览器已经支持了 Pointer Events Level 2 标准,版本更新的 Pointer Events Level 3 已经发布,并且大多数情况下与 Pointer Events Level 2 兼容。
因此,除非你写的代码需要兼容旧版本的浏览器,例如 IE 10 或 Safari 12 或更低的版本,否则无需继续使用鼠标事件或触摸事件 —— 我们可以使用指针事件。
这样,你的代码就可以在触摸设备和鼠标设备上都能正常工作了。
指针事件类型
指针事件 | 类似的鼠标事件 |
---|---|
pointerdown |
mousedown |
pointerup |
mouseup |
pointermove |
mousemove |
pointerover |
mouseover |
pointerout |
mouseout |
pointerenter |
mouseenter |
pointerleave |
mouseleave |
pointercancel |
- |
gotpointercapture |
- |
lostpointercapture |
- |
不难发现,每一个 mouse<event> 都有与之相对应的 pointer<event> 。同时还有 3 个额外的事件没有相应的 mouse... ,我们会在稍后详细解释它们。 |
在代码中用
pointer<event>
替换mouse<event>
我们可以把代码中的mouse<event>
都替换成pointer<event>
,程序仍然正常兼容鼠标设备。
替换之后,程序对触屏设备的支持会“魔法般”地提升。但是,我们可能需要在 CSS 中的某些地方添加touch-action: none
。我们会在下文的pointercancel
一节中描述这里面的细节。
指针事件属性
指针事件具备和鼠标事件完全相同的属性,包括 clientX/Y
和 target
等,以及一些其他属性:
pointerId
—— 触发当前事件的指针唯一标识符。
浏览器生成的。使我们能够处理多指针的情况,例如带有触控笔和多点触控功能的触摸屏(下文会有相关示例)。pointerType
—— 指针的设备类型。必须为字符串,可以是:“mouse”、“pen” 或 “touch”。
我们可以使用这个属性来针对不同类型的指针输入做出不同响应。isPrimary
—— 当指针为首要指针(多点触控时按下的第一根手指)时为true
。
有些指针设备会测量接触面积和点按压力(例如一根手指压在触屏上),对于这种情况可以使用以下属性:width
—— 指针(例如手指)接触设备的区域的宽度。对于不支持的设备(如鼠标),这个值总是1
。height
—— 指针(例如手指)接触设备的区域的长度。对于不支持的设备,这个值总是1
。pressure
—— 触摸压力,是一个介于 0 到 1 之间的浮点数。对于不支持压力检测的设备,这个值总是0.5
(按下时)或0
。tangentialPressure
—— 归一化后的切向压力(tangential pressure)。tiltX
,tiltY
,twist
—— 针对触摸笔的几个属性,用于描述笔和屏幕表面的相对位置。
大多数设备都不支持这些属性,因此它们很少被使用。如果你需要使用它们,可以在 规范文档 中查看更多有关它们的详细信息。
多点触控
多点触控(用户在手机或平板上同时点击若干个位置,或执行特殊手势)是鼠标事件完全不支持的功能之一。
指针事件使我们能够通过 pointerId
和 isPrimary
属性的帮助,能够处理多点触控。
当用户用一根手指触摸触摸屏的某个位置,然后将另一根手指放在该触摸屏的其他位置时,会发生以下情况:
- 第一个手指触摸:
pointerdown
事件触发,isPrimary=true
,并且被指派了一个pointerId
。
- 第二个和后续的更多个手指触摸(假设第一个手指仍在触摸):
pointerdown
事件触发,isPrimary=false
,并且每一个触摸都被指派了不同的pointerId
。
请注意:pointerId
不是分配给整个设备的,而是分配给每一个触摸的。如果 5 根手指同时触摸屏幕,我们会得到 5 个pointerdown
事件和相应的坐标以及 5 个不同的pointerId
。
和第一个触摸相关联的事件总有isPrimary=true
。
利用pointerId
,我们可以追踪多根正在触摸屏幕的手指。当用户移动或抬起某根手指时,我们会得到和pointerdown
事件具有相同pointerId
的pointermove
或pointerup
事件。
事件 pointercancel
pointercancel
事件将会在一个正处于活跃状态的指针交互由于某些原因被中断时触发。也就是在这个事件之后,该指针就不会继续触发更多事件了。
导致指针中断的可能原因如下:
- 指针设备硬件在物理层面上被禁用。
- 设备方向旋转(例如给平板转了个方向)。
- 浏览器打算自行处理这一交互,比如将其看作是一个专门的鼠标手势或缩放操作等。
我们会用一个实际例子来阐释pointercancel
的影响。
例如,我们想要实现一个像 鼠标拖放事件 中开头提到的那样的一个对球的拖放操作。
用户的操作流和对应的事件如下:
- 用户按住了一张图片,开始拖拽
pointerdown
事件触发
- 用户开始移动指针(从而拖动图片)
pointermove
事件触发,可能触发多次
- 然后意料之外的情况发生了!浏览器有自己原生的图片拖放操作,接管了之前的拖放过程,于是触发了
pointercancel
事件。- 现在拖放图片的操作由浏览器自行实现。用户甚至可能会把图片拖出浏览器,放进他们的邮件程序或文件管理器。
- 我们不会再得到
pointermove
事件了。
这里的问题就在于浏览器”劫持“了这一个互动操作:在“拖放”过程开始时触发了pointercancel
事件,并且不再有pointermove
事件会被生成。
阻止浏览器的默认行为来防止 pointercancel
触发。
我们需要做两件事:
- 阻止原生的拖放操作发生:
- 正如我们在 鼠标拖放事件 中描述的那样,我们可以通过设置
ball.ondragstart = () => false
来实现这一需求。 - 这种方式也适用于鼠标事件。
- 正如我们在 鼠标拖放事件 中描述的那样,我们可以通过设置
- 对于触屏设备,还有其他和触摸相关的浏览器行为(除了拖放)。为了避免它们所引发的问题:
- 我们可以通过在 CSS 中设置
#ball { touch-action: none }
来阻止它们。 - 之后我们的代码便可以在触屏设备中正常工作了。
经过上述操作,事件将会按照我们预期的方式触发,浏览器不会劫持拖放过程,也不会触发pointercancel
事件。
- 我们可以通过在 CSS 中设置
指针捕获
指针捕获(Pointer capturing)是针对指针事件的一个特性。
这个想法很简单,但是乍一看可能感觉很奇怪,因为在其他任何事件类型中都没有这种东西。
主要的方法是:
elem.setPointerCapture(pointerId)
—— 将给定的pointerId
绑定到elem
。在调用之后,所有具有相同pointerId
的指针事件都将elem
作为目标(就像事件发生在elem
上一样),无论这些elem
在文档中的实际位置是什么。
换句话说,elem.setPointerCapture(pointerId)
将所有具有给定pointerId
的后续事件重新定位到elem
。
绑定会在以下情况下被移除:- 当
pointerup
或pointercancel
事件出现时,绑定会被自动地移除。 - 当
elem
被从文档中移除后,绑定会被自动地移除。 - 当
elem.releasePointerCapture(pointerId)
被调用,绑定会被移除。
完整起见,这里还需要提及一个知识点。
还有两个与指针捕获相关的事件:
gotpointercapture
会在一个元素使用setPointerCapture
来启用捕获后触发。lostpointercapture
会在捕获被释放后触发:其触发可能是由于releasePointerCapture
的显式调用,或是pointerup
/pointercancel
事件触发后的自动调用。
键盘 keydown keyup
在我们开始学习键盘的相关内容之前,请注意,在现代设备上,还有其他“输入内容”的方法。例如,人们使用语音识别(尤其是在移动端设备上)或用鼠标复制/粘贴。
因此,如果我们想要跟踪 <input>
字段中的所有输入,那么键盘事件是不够的。无论如何,还需要一个名为 input
的事件来跟踪 <input>
字段中的更改。对于这样的任务来说,这可能是一个更好的选择。稍后我们将在 事件:change,input,cut,copy,paste 一章中介绍它们。
当我们想要处理键盘行为时,应该使用键盘事件(虚拟键盘也算)。例如,对方向键 Up 和 Down 或热键(包括按键的组合)作出反应。
Keydown keyup
当一个按键被按下时,会触发 keydown
事件,而当按键被释放时,会触发 keyup
事件。
event.code event.key
事件对象的 key
属性允许获取字符,而事件对象的 code
属性则允许获取“物理按键代码”。
例如,同一个按键 Z,可以与或不与 Shift
一起按下。我们会得到两个不同的字符:小写的 z
和大写的 Z
。
event.key
正是这个字符,并且它将是不同的。但是,event.code
是相同的:
Key | event.key |
event.code |
---|---|---|
Z | z (小写) |
KeyZ |
Shift+Z | Z (大写) |
KeyZ |
如果用户使用不同的语言,那么切换到另一种语言将产生完全不同的字符,而不是 "Z" 。它将成为 event.key 的值,而 event.code 则始终都是一样的:"KeyZ" 。 |
“KeyZ” 和其他按键代码
每个按键的代码都取决于该按键在键盘上的位置。UI 事件代码规范 中描述了按键代码。
例如:
- 字符键的代码为
"Key<letter>"
:"KeyA"
,"KeyB"
等。- 数字键的代码为:
"Digit<number>"
:"Digit0"
,"Digit1"
等。- 特殊按键的代码为按键的名字:
"Enter"
,"Backspace"
,"Tab"
等。
有几种广泛应用的键盘布局,该规范给出了每种布局的按键代码。
有关更多按键代码,请参见 规范的字母数字部分
大小写敏感:
"KeyZ"
,不是"keyZ"
这是显而易见的,但人们仍会搞错。
请规避错误类型:它是KeyZ
,而不是keyZ
。像event.code=="keyZ"
这样的检查不起作用:"Key"
的首字母必须大写。
如果按键没有给出任何字符呢?例如,Shift 或 F1 或其他。对于这些按键,它们的 event.key
与 event.code
大致相同:
Key | event.key |
event.code |
---|---|---|
F1 | F1 |
F1 |
Backspace | Backspace |
Backspace |
Shift | Shift |
ShiftRight 或 ShiftLeft |
请注意,event.code
准确地标明了哪个键被按下了。例如,大多数键盘有两个 Shift 键,一个在左边,一个在右边。event.code
会准确地告诉我们按下了哪个键,而 event.key
对按键的“含义”负责:它是什么(一个 “Shift”)。
event.code
可能由于意外的键盘布局而与错误的字符进行了匹配。不同键盘布局中的相同字母可能会映射到不同的物理键,从而导致了它们有不同的代码。幸运的是,这种情况只发生在几个代码上,例如 keyA
,keyQ
,keyZ
(我们已经看到了),而对于诸如 Shift
这样的特殊按键没有发生这种情况。你可以在 规范 中找到该列表。
为了可靠地跟踪与受键盘布局影响的字符,使用 event.key
可能是一个更好的方式。
另一方面,event.code
的好处是,绑定到物理键位置的 event.code
会始终保持不变。因此,即使在切换了语言的情况下,依赖于它的热键也能正常工作。
我们想要处理与布局有关的按键?那么 event.key
是我们必选的方式。
或者我们希望一个热键即使在切换了语言后,仍能正常使用?那么 event.code
可能会更好。
自动重复
如果按下一个键足够长的时间,它就会开始“自动重复”:keydown
会被一次又一次地触发,然后当按键被释放时,我们最终会得到 keyup
。因此,有很多 keydown
却只有一个 keyup
是很正常的。
对于由自动重复触发的事件,event
对象的 event.repeat
属性被设置为 true
。
默认行为
默认行为各不相同,因为键盘可能会触发很多可能的东西。
例如:
- 出现在屏幕上的一个字符(最明显的结果)。
- 一个字符被删除(Delete 键)。
- 滚动页面(PageDown 键)。
- 浏览器打开“保存页面”对话框(Ctrl+S)
- ……等。
阻止对keydown
的默认行为可以取消大多数的行为,但基于 OS 的特殊按键除外。例如,在 Windows 中,Alt+F4 会关闭当前浏览器窗口。并且无法通过在 JavaScript 中阻止默认行为来阻止它。
滚动
scroll
事件允许对页面或元素滚动作出反应。我们可以在这里做一些有用的事情。
例如:
- 根据用户在文档中的位置显示/隐藏其他控件或信息。
- 当用户向下滚动到页面末端时加载更多数据。
防止滚动
我们如何使某些东西变成不可滚动?
我们不能通过在 onscroll
监听器中使用 event.preventDefault()
来阻止滚动,因为它会在滚动发生 之后 才触发。
但是我们可以在导致滚动的事件上,例如在 pageUp 和 pageDown 的 keydown
事件上,使用 event.preventDefault()
来阻止滚动。
如果我们向这些事件中添加事件处理程序,并向其中添加 event.preventDefault()
,那么滚动就不会开始。
启动滚动的方式有很多,使用 CSS 的 overflow
属性更加可靠。
请注意滚动的两个重要特性:
- 滚动是“弹性的”。在某些浏览器/设备中,我们可以在文档的顶端或末端稍微多滚动出一点(超出部分显示的是空白区域,然后文档将自动“弹回”到正常状态)。
- 滚动并不精确。当我们滚动到页面末端时,实际上我们可能距真实的文档末端约 0-50px。