目录
- JavaScript 事件简介
- 常用浏览器事件
- Event
- addEventListener
- removeEventListener
- event对象
- 冒泡和捕获
- 冒泡
- DOM事件传播的3个阶段、捕获
- 事件委托
- 浏览器默认行为
- 阻止浏览器默认行为
- 处理程序选项passive
- event.defaultPrevented
- UI事件
- 鼠标事件
- 常用鼠标事件:
- 触发顺序
- 鼠标按钮
- 组合键:`shift`、`alt`、`ctrl`、`meta`
- 坐标
- 防止鼠标按下时选择
- 指针事件
- 常用指针事件
- 指针事件的属性
- 事件:ponitercancel
- 指针捕获
- 键盘事件
- 滚动
- 表单、控件
- 表单命名与获取
- 表单元素
- 聚焦:focus/blur
- 事件change,input,cut,copy,paste
- 表单事件和方法提交
- 表单事件和方法提交
JavaScript 事件简介
常用浏览器事件
鼠标事件:
click
—— 当鼠标点击一个元素时(触摸屏设备会在点击时生成)。dbclick
——在短时间内双击同一元素后触发。如今已经很少使用了contextmenu
—— 当鼠标右键点击一个元素时。mouseover
/mouseout
—— 当鼠标指针移入/离开一个元素时。mousedown
/mouseup
—— 当在元素上按下/释放鼠标按钮时。mousemove
—— 当鼠标移动时。
键盘事件:
keydown
和keyup
—— 当按下和松开一个按键时。
表单(form)元素事件:
submit
—— 当访问者提交了一个<form>
时。focus
—— 当访问者聚焦于一个元素时,例如聚焦于一个<input>
。
Document 事件:
DOMContentLoaded
—— 当 HTML 的加载和处理均完成,DOM 被完全构建完成时。
CSS 事件:
transitionend
—— 当一个 CSS 动画完成时
Event
addEventListener
-
语法:
element.addEventListener(event, handler[, options]);
-
参数:
event
:事件名handler
:处理程序- options:可选参数
once
:如果为true
,那么会在被触发后自动删除监听器。capture
:事件处理的阶段,捕获于冒泡false/true
,它与{capture: false/true}
相同。passive
:如果为true
,那么处理程序将不会调用preventDefault()
- 这里的
handler
也可以是对象/类,只要它包含对象处理程序handleEvent
,必须是这个命名。当addEventListener
接收一个对象作为处理程序时,在事件发生时,它就会调用obj.handleEvent(event)
来处理事件
<button id="elem">Click me</button>
<script>
let obj = {
handleEvent(event) {
alert(event.type + " at " + event.currentTarget);
}
};
elem.addEventListener('click', obj);
</script>
removeEventListener
- 语法:
element.removeEventListener(event, handler[, options]);
- 注意:要移除处理程序,我们需要传入与分配的函数完全相同的函数,因此箭头函数定义的监听器将无法移除,因为地址不同
event对象
当事件发生时,浏览器会创建一个 event
对象,将详细信息放入其中,并将其作为参数传递给处理程序:
event.type
——事件类型event.currentTarget
——处理事件的元素。这与this
相同,除非处理程序是一个箭头函数,或者它的this
被绑定到了其他东西上,之后我们就可以从event.currentTarget
获取元素了event.clientX / event.clientY
——指针事件(pointer event)的指针的窗口相对坐标- 其他
冒泡和捕获
冒泡
-
冒泡(bubbling):(目标元素→父元素)当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序
-
冒泡中的
event.target
:父元素上的处理程序始终可以获取事件实际发生位置的详细信息。引发事件的那个嵌套层级最深的元素被称为目标元素,可以通过
event.target
访问。它与this
(也是event.currentTarget
)之间是有区别的:event.target
—— 是引发事件的“目标”元素,它在冒泡过程中不会发生变化。this
—— 是“当前”元素,其中有一个当前正在运行的处理程序
-
停止冒泡:
冒泡事件从目标元素开始向上冒泡。通常,它会一直上升到
<html>
,然后再到document
对象,有些事件甚至会到达window
,它们会调用路径上所有的处理程序。但是任意处理程序都可以决定事件已经被完全处理,并停止冒泡。用于停止冒泡的方法是event.stopPropagation()
。
<body onclick="alert(`the bubbling doesn't reach here`)">
<button onclick="event.stopPropagation()">Click me</button>
</body>
通常,没有真正的必要去阻止冒泡
DOM事件传播的3个阶段、捕获
-
捕获阶段(Capturing phase)—— 事件(从 Window)向下走近元素。
-
目标阶段(Target phase)—— 事件到达目标元素。
-
冒泡阶段(Bubbling phase)—— 事件从元素上开始冒泡
表格中点击
<td>
的图片时DOM传播过程:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4DEHmJMa-1637770704979)(C:\Users\HeHao\AppData\Roaming\Typora\typora-user-images\image-20211119005232611.png)]
为了在捕获阶段捕获事件,我们需要将处理程序的 capture
选项设置为 true
:elem.addEventListener(..., {capture: true})
或elem.addEventListener(..., true)
apture
选项有两个可能的值:
- 如果为
false
(默认值),则在冒泡阶段设置处理程序。 - 如果为
true
,则在捕获阶段设置处理程序。
注意可以给事件click添加两个相同的方法,那么就又可以冒泡又可以捕获了
事件委托
-
什么是事件委托
JavaScript事件委托也叫事件代理,实际上就是通过冒泡/捕获机制将一个元素的响应事件委托给另外一个元素,支持位同一个DOM注册多个同类型事件
-
事件委托的优点:
- 减少内存消耗,提高性能
- 可以动态绑定事件
浏览器默认行为
阻止浏览器默认行为
- 主流的方式是使用
event
对象。有一个event.preventDefault()
方法 - 如果处理程序是使用
on<event>
(而不是addEventListener
)分配的,那返回false
也同样有效
<a href="/" onclick="return false">Click here</a>
<a href="/" onclick="event.preventDefault()">here</a>
处理程序选项passive
-
addEventListener
的可选项passive: true
向浏览器发出信号,表明处理程序将不会调用preventDefault()
。 -
为什么需要这样做?移动设备上会发生一些事件,例如
touchmove
(当用户在屏幕上移动手指时),默认情况下会导致滚动,但是可以使用处理程序的preventDefault()
来阻止滚动。因此,当浏览器检测到此类事件时,它必须首先处理所有处理程序,然后如果没有任何地方调用preventDefault
,则页面可以继续滚动。但这可能会导致 UI 中不必要的延迟和“抖动”。passive: true
选项告诉浏览器,处理程序不会取消滚动。然后浏览器立即滚动页面以提供最大程度的流畅体验,并通过某种方式处理事件。对于某些浏览器(Firefox,Chrome),默认情况下,touchstart
和touchmove
事件的passive
为true
。
event.defaultPrevented
如果默认行为被阻止,那么 event.defaultPrevented
属性为 true
,否则为 false
。
UI事件
鼠标事件
常用鼠标事件:
click
—— 当鼠标点击一个元素时(触摸屏设备会在点击时生成)。dbclick
——在短时间内双击同一元素后触发。如今已经很少使用了contextmenu
—— 当鼠标右键点击一个元素时。mouseover
/mouseout
—— 当鼠标指针移入/离开一个元素时。over、out、enter、leavemousedown
/mouseup
—— 当在元素上按下/释放鼠标按钮时。mousemove
—— 当鼠标移动时。
触发顺序
遵循 mousedown
→ mouseup
→ click
鼠标按钮
鼠标按键状态 | event.button |
---|---|
左键 (主要按键) | 0 |
中键 (辅助按键) | 1 |
右键 (次要按键) | 2 |
X1 键 (后退按键) | 3 |
X2 键 (前进按键) | 4 |
组合键:shift
、alt
、ctrl
、meta
-
shiftKey
:Shift -
altKey
:Alt(或对于 Mac 是 Opt) -
ctrlKey
:Ctrl -
metaKey
:对于 Mac 是 Cmd
<button id="button">Alt+Shift+Click on me!</button>
<script>
button.onclick = function(event) {
if (event.altKey && event.shiftKey) {
alert('Hooray!');
}
};
</script>
坐标
所有的鼠标事件都提供了两种形式的坐标:
-
相对于窗口的坐标:
clientX
和clientY
。 -
相对于文档的坐标:
pageX
和pageY
防止鼠标按下时选择
-
双击鼠标会有副作用,在某些界面中可能会出现干扰:它会选择文本。在这种情况下,最合理的方式是防止浏览器对
mousedown
进行操作。 -
**防止复制:**如果我们想禁用选择以保护我们页面的内容不被复制粘贴,那么我们可以使用另一个事件:
oncopy
。
<div oncopy="alert('Copying forbidden!');return false">
Dear user,
The copying is forbidden for you.
If you know JS or HTML, then you can get everything from the page source though.
</div>
指针事件
常用指针事件
指针事件 | 类似的鼠标事件 |
---|---|
pointerdown | mousedown |
pointerup | mouseup |
pointermove | mousemove |
pointerover | mouseover |
pointerout | mouseout |
pointerenter | mouseenter |
pointerleave | mouseleave |
pointercancel | - |
gotpointercapture | - |
lostpointercapture | - |
指针事件的属性
指针事件具备和鼠标事件完全相同的属性,包括 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
—— 针对触摸笔的几个属性,用于描述笔和屏幕表面的相对位置。
大多数设备都不支持这些属性,因此它们很少被使用。如果你需要使用它们, 可以翻阅规范文档
事件:ponitercancel
pointercancel
事件将会在一个正处于活跃状态的指针交互由于某些原因被中断时触发。也就是在这个事件之后,该指针就不会继续触发更多事件了。自定义拖放事件必须阻止浏览器默认指针事件!可以通过JSelem.ondragstart = () => false
或者CSS的elem { touch-action: none }
导致指针中断的可能原因如下:
- 指针设备硬件在物理层面上被禁用。
- 设备方向旋转(例如给平板转了个方向)。
- 浏览器打算自行处理这一交互,比如将其看作是一个专门的鼠标手势或缩放操作等。
指针捕获
elem.setPointerCapture(pointerId)
—— 将给定的 pointerId
绑定到 elem
。在调用之后,所有具有相同 pointerId
的指针事件都将 elem
作为目标(就像事件发生在 elem
上一样),无论这些 elem
在文档中的实际位置是什么。
绑定会在以下情况下被移除:
- 当
pointerup
或pointercancel
事件出现时,绑定会被自动地移除。 - 当
elem
被从文档中移除后,绑定会被自动地移除。 - 当
elem.releasePointerCapture(pointerId)
被调用,绑定会被移除
键盘事件
- keydown
- keyup
滚动
-
scroll
事件允许对页面或元素滚动作出反应。我们可以在这里做一些有用的事情。 -
防止滚动:**不能通过在
onscroll
监听器中使用event.preventDefault()
来阻止滚动,因为它会在滚动发生之后才触发。但可以处理导致滚动的事件防止滚动**,例如在 pageUp 和 pageDown 的keydown
事件上,使用event.preventDefault()
来阻止滚动 -
启动滚动:方法很多,使用 CSS 的
overflow
属性更加可靠
表单、控件
表单命名与获取
- 文档中的表单是特殊集合
document.forms
的成员。这就是所谓的“命名的集合”,既是被命名了的,也是有序的。我们既可以使用名字,也可以使用在文档中的编号来获取表单。
document.forms.my - name="my" 的表单
document.forms[0] - 文档中的第一个表单
- 可能会有多个名字相同的元素,这种情况经常在处理单选按钮中出现。在这种情况下,
form.elements[name]
将会是一个集合,例如:
<form>
<input type="radio" name="age" value="10">
<input type="radio" name="age" value="20">
</form>
<script>
let form = document.forms[0];
let ageElems = form.elements.age;
alert(ageElems[0]); // [object HTMLInputElement]
</script>
-
对于任何元素,其对应的表单都可以通过
element.form
访问到。因此,表单引用了所有元素,元素也引用了表单。 -
反向引用:对于任何元素,其对应的表单都可以通过
element.form
访问到。因此,表单引用了所有元素,元素也引用了表单。
表单元素
-
input和textarea:
我们可以通过
input.value
(字符串)或input.checked
(布尔值)来访问复选框(checkbox)中的它们的value
。 -
select和option:
一个
<select>
元素有 3 个重要的属性:select.options
——<option>
的子元素的集合,select.value
—— 当前所选择的<option>
的value
,select.selectedIndex
—— 当前所选择的<option>
的编号。
它们提供了三种为
<select>
设置value
的不同方式:- 找到对应的
<option>
元素,并将option.selected
设置为true
。 - 将
select.value
设置为对应的value
。 - 将
select.selectedIndex
设置为对应<option>
的编号。
<select id="select">
<option value="apple">Apple</option>
<option value="pear">Pear</option>
<option value="banana">Banana</option>
</select>
<script>
// 所有这三行做的是同一件事
select.options[2].selected = true;
select.selectedIndex = 2;
select.value = 'banana';
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZTjyOp1O-1637770704982)(C:\Users\HeHao\AppData\Roaming\Typora\typora-user-images\image-20211124234827771.png)]
聚焦:focus/blur
-
事件
-
elem.onfocus = func
-
elem.onblur = func
-
-
方法
- elem.focus() 使成为焦点
- elem.blur() 使失去焦点
-
允许在任何元素上聚焦:tabindex
默认情况下,很多元素不支持聚焦。任何浏览器中
focus/blur
都支持这些用户可以交互的元素如<button>
,<input>
,<select>
,<a>
等。但一些诸如<div>
、<span>
等元素却不能,但是我们可以使用HTML-特性(attribute)tabindex
可以改变这种情况。任何具有tabindex
特性的元素,都会变成可聚焦的。该特性的value
是当使用 Tab(或类似的东西)在元素之间进行切换时,元素的顺序号。tabindex
的值决定Tab键跳转的顺序 -
委托
focus
和blur
事件不会向上冒泡。目前又两个解决方案:- 捕获
- 使用
focusin
和focusout
事件 —— 与focus/blur
事件完全一样,只是它们会冒泡。但必须使用elem.addEventListener
来分配它们,而不是on<event>
事件change,input,cut,copy,paste
- change:当元素更改完成时,将触发
change
事件。对于文本输入框,当其失去焦点时,就会触发change
事件。对于其它元素:select
,input type=checkbox/radio
,会在选项更改后立即触发change
事件。 - input:每当用户对输入值进行修改后,就会触发
input
事件,无法使用event.preventDefault()
阻止事件发生 - cut,copy,paste:可以使用
event.preventDefault()
来中止行为。event.clipboardData
属性可以用于读/写剪贴板。
表单事件和方法提交
提交表单时,会触发 submit
事件,它通常用于在将表单发送到服务器之前对表单进行校验,或者中止提交,并使用 JavaScript 来处理表单
-
submit事件
提交表单主要有两种方式:
- 点击
<input type="submit">
或<input type="image">
- 在
input
字段中按下 Enter 键。
这两个行为都会触发表单的
submit
事件。处理程序可以检查数据,如果有错误,就显示出来,并调用event.preventDefault()
,这样表单就不会被发送到服务器了。 - 点击
-
submit方法
如果要手动将表单提交到服务器,我们可以调用
form.submit()
。这样就不会产生submit
事件。
属性可以用于读/写剪贴板。
表单事件和方法提交
提交表单时,会触发 submit
事件,它通常用于在将表单发送到服务器之前对表单进行校验,或者中止提交,并使用 JavaScript 来处理表单
-
submit事件
提交表单主要有两种方式:
- 点击
<input type="submit">
或<input type="image">
- 在
input
字段中按下 Enter 键。
这两个行为都会触发表单的
submit
事件。处理程序可以检查数据,如果有错误,就显示出来,并调用event.preventDefault()
,这样表单就不会被发送到服务器了。 - 点击
-
submit方法
如果要手动将表单提交到服务器,我们可以调用
form.submit()
。这样就不会产生submit
事件。