by caix in 深圳
事件对象
DOM 中发生事件时,所有的相关信息都会被收集并存储在一个名为event的对象中
这个对象包含了一些基本的信息,比如导致事件的元素,发生的事件类型,以及可能与特定事件相关的任何其他数据,所有的浏览器都支持这个对象,尽管支持的方式不同
DOM 在触发某个事件的时候,会产生一个包含事件相关信息的event事件对象,event 包含着所有与事件有关的信息。如导致事件的元素、事件的类型等
DOM 中的事件对象
在 DOM 合规的浏览器中,event对象是传给事件处理程序的唯一参数,不管以哪种方式指定的,都会传递它
let btn = document.getElementById('btn');
btn.onclick = function(event){
console.log(event.type); => click
}
btn.addEventListener('click', function(event){
console.log(event.type); => click
}, false)
可用的属性和方法
属性/方法 | 类型 | 说明 |
---|---|---|
bubbles | Boolean | 表明事件是否冒泡 |
cancelable | Boolean | 表明是否可以取消事件的默认行为 |
currentTarget | Element | 当前正在处理事件的那个元素 |
defaultPrevented | Boolean | 为 true 表示已经调用了 preventDefault() |
detail | Integer | 与事件相关的细节信息 |
eventPhase | Integer | 调用事件处理程序的阶段;1:捕获阶段,2:处于目标,2:冒泡阶段 |
preventDefault() | Function | 取消事件的默认行为 |
stopPropagation() | Function | 取消事件的进一步捕获或冒泡 |
target | Element | 事件的目标 |
type | String | 被触发的事件类型 |
事件对象包含与特定事件相关的属性和方法,不同的事件生成的事件对象也会包含不同的属性和方法。不过,所有的事件对象都会包含下面几个常用的字段
currentTarget 当前事件处理程序所在的元素
preventDefault() 用于取消事件的默认行为
stopPropagation() 用于取消所有后续事件捕获或事件冒泡
target 事件目标
type 事件类型
在事件处理程序内部,this 对象始终等于 currentTarget 的值,targer 只包含事件的实际目标
this 是事件绑定的元素(绑定这个事件处理函数的元素
event.target 是事件触发的元素
IE 事件对象
与 DOM 对象不同的是,IE 事件对象可以基于事件处理程序被指定的方式以不同的方式来访问
如果事件处理程序是使用 DOM0 方式指定的,则 event 对象只是 window 对象的一个属性
如果是使用 attachEvent 指定的,则 event 对象会作为唯一的参数传递给处理函数
let btn = document.getElementById('btn');
btn.onclick = function(){
let event = window.event;
console.log(event.type);
}
let btn = document.getElementById('btn');
btn.attachEvent('onclick', function(event){
console.log(event.type);
})
可用的属性和方法
属性/方法 | 类型 | 说明 |
---|---|---|
cancelBubble | Boolean | 默认值为 false,为 true 时就是取消事件冒泡 |
returnValue | Boolean | 默认值为 true,为 false 时取消事件的默认行为 |
srcElement | Element | 事件的目标 |
type | String | 被触发的事件类型 |
IE对象也包含与导致其创建的特定事件相关的属性和方法
cancelBubble 默认false,true 时可以取消冒泡,和 DOM 的
stopPropagation 方法相同
returnValue 默认为 true 设置为 false 可以取消事件默认行为,与 DOM 的 preventDefault 相同
srcElement 事件目标,类似 DOM 中的 target
由于事件处理程序的作用域取决于指定它的方式,因此 this 的值并不总是等于事件目标,所以更好的方式是使用事件对象的srcElement 属性代替 this
let btn = document.getElementById('ben');
btn.onclick = function(event){
console.log(window.event.srcElement === this);
=>true
}
btn.attachEvent('onclick', function(event){
console.log(event.srcElement === this) => false
})
事件对象 的一些兼容写法
获得 event对象 兼容性写法
event || (event = window.event);
获得 target 兼容型写法
event.target||event.srcElement
阻止浏览器默认行为兼容性写法
event.preventDefault ? event.preventDefault() : (event.returnValue = false);
阻止冒泡写法
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = true);
跨浏览器事件对象
var EventUtil = {
getEvent:function(event){
// 事件对象
return event ? event:window.event
},
getTarget:function(evet){
// 事件目标
return event.target || event.srcElement;
},
prventDefault:function(event){
// 取消事件默认行为
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue=false;
}
},
stopPropagation:function(event){
// 取消事件进一步冒泡或捕获
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
}
使用方法,假设想返回事件目标
btn.onclick=function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event)
}
事件类型
Web 浏览器中可能发生的事件有很多类型。如前所述,不同的事件类型具有不同的信息,而 “DOM3 级事件”规定了以下几类事件
UI(User Interface,用户界面)事件,当用户与页面上的元素交互时触发
焦点事件,当元素获得或失去焦点时触发
鼠标事件,当用户通过鼠标在页面上执行操作时触发
滚轮事件,当使用鼠标滚轮(或类似设备)时触发
文本事件,当在文档中输入文本时触发
键盘事件,当用户通过键盘在页面上执行操作时触发
合成事件,当为 IME(Input Method Editor,输入法编辑器)输入字符时触发
变动(mutation)事件,当底层 DOM结构发生变化时触发
变动名称事件,当元素或属性名变动时触发。此类事件已经被废弃,没有任何浏览器实现它们, 因此本章不做介绍
除了这几类事件之外,HTML5也定义了一组事件,而有些浏览器还会在 DOM和 BOM中实现其他 专有事件
三类重点事件类型
键盘事件
事件名 | 发生时机 |
---|---|
onkeydown | 键盘按键按下 |
keypress | 键盘按键按住 |
keyup | 键盘按键松开 |
监听某个按键事件
document.onkeydown = function(event) {
// 键盘按下时触发
console.log('key down');
};
document.onkeypress = function(event) {
// 键盘按住时触发
console.log('key press');
};
document.onkeyup = function (event) {
// 键盘弹起时触发
console.log('key up');
};
event参数 该参数为 KeyboardEvent 事件对象,其中包含按键相关的一些属性
type:事件类型
key:表示按下的键盘内容是什么即键值,按下字母 'p' 时,值为'p'
code:表示键盘代码,按下字母 'p' 时,值为 'KeyP'
keyCode:整数,表示键码,每个键都有唯一的键码,字母 'p' 的键码为80
altKey:布尔值,表示此时的 alt 键是否也按下
ctrKey:布尔值,表示此时的 ctr 键是否也按下
shiftKey:布尔值,表示此时的 shift 键是否也按下
metaKey:布尔值,windows 平台表示 Window 键是否同时按下,mac表示Command键是否同时按下
repeat: 布尔值,如果一个键一直被按着,则其值为true,表示重复
document.onkeydown = function(event) {
// 键盘按下是触发
console.log('key down: ' + event.key);
if (event.altKey) {
console.log('alt is active');
}
if (event.shiftKey) {
console.log('shift is active');
}
};
鼠标事件
事件名 | 发生时机 |
---|---|
onclick | 鼠标单击对象时触发的事件 |
ondblclick | 鼠标双击对象时触发的事件 |
onmousedown | 鼠标按钮被按下时触发的事件 |
onmousemove | 鼠标被移动时触发的事件 |
onmouseout | 鼠标离开监听该事件的元素或子元素时触发的事件 |
onmouseover | 鼠标移动到监听该事件的元素或子元素时触发的事件 |
onmouseup | 鼠标按键被松开时触发的事件 |
代码示例
<script type="text/javascript">
function appendText(str) {
document.body.innerHTML += str + "<br/>";
}
document.onmousedown = function() {
appendText("onmousedown");
appendText("button = " + event.button);
appendText("(x,y) = " + event.x + "," + event.y);
}
document.onmouseup = function() {
appendText("onmouseup");
}
document.onclick = function() {
appendText("onclick");
}
document.ondblclick = function() {
appendText("ondblclick");
}
document.oncontextmenu = function() {
appendText("oncontextmenu");
}
document.onmouseover = function() {
appendText("onmouseover");
}
document.onmouseout = function() {
appendText("onmouseout");
}
document.onmousemove = function() {
appendText("mousemove");
}
</script>
触发时的参数为 MouseEvent 对象类型,MouseEvent对象中包含下面比较有用的属性:
type: 事件类型,如 mosemove 或者mousedown
button:整型,触发鼠标事件时按下的按钮编号: button = 1(鼠标左键),button = 2(鼠标右键),button = 0(鼠标中间键)
buttons:整型,触发鼠标事件时弹起来的按钮编号
clientX:鼠标指针在 DOM 内容区的X坐标
clientY:鼠标指针在 DOM 内容区的Y坐标
offsetX:鼠标指针相对父节点填充边缘的X坐标
offsetY: 鼠标指针相对父节点填充边缘的Y坐标
screenX: 鼠标指针在全局屏幕的X坐标
screenY: 鼠标指针在全局屏幕的Y坐标
pageX: 鼠标指针在整个DOM内容(包括分页)的X坐标
pageY: 鼠标指针在整个DOM内容(包括分页)的Y坐标
altKey: 布尔值,表示此时的alt键是否也按下
ctrKey: 布尔值,表示此时的alt键是否也按下
shiftKey: 布尔值,表示此时的shift键是否也按下
metaKey: 布尔值,windows平台表示 Window键 是否同时按下,mac 表示Command键是否同时按下
document.oncontextmenu = function(){
return false
}; //禁止鼠标右键菜单显示
var res = document.getElementById('box'); // 找到id为box的div
document.body.onmouseup = function(e){ // 在body里点击触发事件
if(e.button === 2){
// 如果button=1(鼠标左键),button=2(鼠标右键),button=0(鼠标中间键)
console.log(e); //将传进去的参数打印出来
console.log(e.offsetY); //打印出鼠标点击的Y轴坐标
console.log(e.offsetX); //打印出鼠标点击的X轴坐标
res.style.top = e.offsetY+'px'; //鼠标点击时给div定位Y轴
res.style.left = e.offsetX+'px'; //鼠标点击时给div定位X轴
res.style.display = 'block'; //显示div盒子
}else{
res.style.display = 'none'; //否则不显示div盒子
}
}
焦点事件
不是所有元素都有焦点事件,只有可交互性的元素才有,比如表单元素,a标签
页面中只能有一个元素有焦点,一个聚焦,另一个就失焦,默认在document
这一类事件中主要的两个是 focus 和 blur,它们都是 JavaScript早期就得到所有浏览器支持的 事件
<form>
<input type="text" name="txt1" id="txt" />
<input type="button" name="btn" value="点击" />
</form>
form.txt1.focus(); => 让元素获得焦点,该方法不会触发 onfocus()事件
form.txt1.onfocus=function(){
console.log(1);
}
=> 元素获得焦点时会触发该事件
form.txt1.onblur=function(){
console.log(2);
}
=> 元素失去焦点时触发该事件
form.btn.onclick=function(){
form.txt1.select();
}
=> 选中输入框中的所有文字
form.btn.onclick=function(){
form.txt1.setSelectionRange(2,5) ;
form.txt1.focus();
}
=> setSelectionRange 需要配合着focus()使用才看得到效果,其中setSelectionRange的结束位置不包含在内,setSelectionRange(start,end)包含两个参数,一个是start:起始位置;一个是end:结束位置。
其他事件类型
UI事件
UI事件 指的是那些不一定与用户操作有关的事件。这些事件在 DOM规范出现之前,都是以这种或 那种形式存在的,而在 DOM规范中保留是为了向后兼容。现有的 UI事件如下:
DOMActivate:表示元素已经被用户操作(通过鼠标或键盘)激活。这个事件在 DOM3 级事 件中被废弃,但 Firefox 2+和 Chrome支持它。考虑到不同浏览器实现的差异,不建议使用这个 事件。
load:当页面完全加载后在 window 上面触发,当所有框架都加载完毕时在框架集上面触发, 当图像加载完毕时在元素上面触发,或者当嵌入的内容加载完毕时在元素上面 触发。
unload:当页面完全卸载后在 window 上面触发,当所有框架都卸载后在框架集上面触发,或 者当嵌入的内容卸载完毕后在元素上面触发。
abort:在用户停止下载过程时,如果嵌入的内容没有加载完,则在元素上面触发。
error:当发生 JavaScript错误时在 window 上面触发,当无法加载图像时在元素上面触 发,当无法加载嵌入内容时在元素上面触发,或者当有一或多个框架无法加载时在框 架集上面触发。第 17章将继续讨论这个事件。
select:当用户选择文本框(或)中的一或多个字符时触发。第 14章将 继续讨论这个事件。
resize:当窗口或框架的大小变化时在 window 或框架上面触发。
scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。元素中包含所加 载页面的滚动条。
复合事件
复合事件(composition event)是 DOM3级事件中新添加的一类事件,用于处理 IME 的输入序列。 IME(Input Method Editor,输入法编辑器)可以让用户输入在物理键盘上找不到的字符。例如,使用拉 丁文键盘的用户通过 IME 照样能输入日文字符。IME 通常需要同时按住多个键,但终只输入一个字 符。复合事件就是针对检测和处理这种输入而设计的。有以下三种复合事件:
compositionstart:在 IME 的文本复合系统打开时触发,表示要开始输入了。
compositionupdate:在向输入字段中插入新字符时触发。
compositionend:在 IME 的文本复合系统关闭时触发,表示返回正常键盘输入状态。 复合事件与文本事件在很多方面都很相似。
在触发复合事件时,目标是接收文本的输入字段。但它 比文本事件的事件对象多一个属性 data,其中包含以下几个值中的一个:
如果在 compositionstart 事件发生时访问,包含正在编辑的文本(例如,已经选中的需要马 上替换的文本);
如果在 compositionupdate 事件发生时访问,包含正插入的新字符;
如果在 compositionend 事件发生时访问,包含此次输入会话中插入的所有字符。
变动事件
DOM2级的变动(mutation)事件能在 DOM中的某一部分发生变化时给出提示。变动事件是为 XML 或 HTML DOM设计的,并不特定于某种语言。DOM2级定义了如下变动事件:
DOMSubtreeModified:在 DOM 结构中发生任何变化时触发。这个事件在其他任何事件触发 后都会触发。
DOMNodeInserted:在一个节点作为子节点被插入到另一个节点中时触发。
DOMNodeRemoved:在节点从其父节点中被移除时触发。
DOMNodeInsertedIntoDocument:在一个节点被直接插入文档或通过子树间接插入文档之后 触发。这个事件在 DOMNodeInserted之后触发。
DOMNodeRemovedFromDocument:在一个节点被直接从文档中移除或通过子树间接从文档中移 除之前触发。这个事件在 DOMNodeRemoved 之后触发。
DOMAttrModified:在特性被修改之后触发。
DOMCharacterDataModified:在文本节点的值发生变化时触发。
HTML5事件
contextmenu 事件
beforeunload 事件
DOMContentLoaded 事件
readystatechange 事件
pageshow 和 pagehide 事件
hashchange 事件
设备事件
orientationchange 事件
MozOrientation 事件
deviceorientation 事件
devicemotion 事件
HTML事件
由 HTML 表单内部 的动作触发的事件
onblur script:当元素失去焦点时触发
onchange script:当元素改变时触发
oncontextmenu script:当触发上下文菜单时触发
onfocus script:当元素获得焦点时触发
onformchange script:当表单改变时触发
onforminput script:当表单获得用户输入时触发
oninput script:当元素获得用户输入时触发
oninvalid script:当元素无效时触发
onreset script:当表单重置时触发
onselect script:当选取元素时触发
onsubmit script:当提交表单时触发
触摸事件
当手指放在屏幕上、在屏幕上滑动或从屏幕移开时,触摸事件即会触发。触摸事件有如下几种
touchstart:手指放到屏幕上时触发(即使有一个手指已经放在了屏幕上)
touchmove:手指在屏幕上滑动时连续触发。在这个事件中调用
preventDefault() 可以阻止滚动
touchend:手指从屏幕上移开时触发
touchcancel:系统停止跟踪触摸时触发。有几种可能的原因如下
由于某个事件出现而取消了触摸:例如触摸过程被弹窗打断。
触点离开了文档窗口,而进入了浏览器的界面元素、插件或者其他外部内容区域。
当用户产生的触点个数超过了设备支持的个数,从而导致 TouchList 中最早的 Touch 对象被取消
每个触摸事件的 event 对象都提供了鼠标事件的公共属性,除了这些公共的 DOM 属性,触摸事件还提供了以下 3 个属性用于跟踪触点
touches:Touch 对象的数组,表示当前屏幕上的每个触点
targetTouches:Touch 对象的数组,表示特定于事件目标的触点
changedTouches:Touch 对象的数组,表示自上次用户动作之后变化的触点
手势事件
手势事件会在两个手指触碰屏幕且相对距离或旋转角度变化时触发。手势事件有以下 3 种:
gesturestart:一个手指已经放在屏幕上,再把另一个手指放到屏幕上时触发
gesturechange:任何一个手指在屏幕上的位置发生变化时触发
gestureend:其中一个手指离开屏幕时触发
只有在两个手指同时接触事件接收者时,这些事件才会触发
在一个元素上设置事件处理程序,意味着两个手指必须都在元素边界以内才能触发手势事件(这个元素就是事件目标)
事件循环 EVENT LOOP (事件执行机制)
js是一门单线程的编程语言,也就是说js在处理任务的时候,所有任务只能在一个线程上排队被执行,那如果某一个任务耗时比较长呢?总不能等到它执行结束再去执行下一个。 所以在线程之内,又被分为了两个队列:
同步任务队列
异步任务队列
这里说的异步任务,它的意思是包含了独立于主执行栈之外的 宏任务 和 微任务
Javascript 有一个 主线程 (main thread ) 和 调用栈 (call-stack 执行栈),所有的任务都会被放到 调用栈 等待 主线程 执行
简单示例
console.log('start')
setTimeout(function() {
console.log('setTimeout')
}, 0)
console.log('end')
这样的情况,函数调用栈执行到setTimeout时,setTimeout会在规定的时间点将回调函数放入异步队列,等待同步队列的任务被执行完,立即执行
所以结果是:start、end、setTimeout
但需要注意的一点是,普遍认为setTimeout定时执行的认知是片面的,因为假设setTimeout规定2秒后执行,但同步队列中有一个函数,执行花了很长时间,甚至花了1秒
那么这时setTimeout中的回调也会等上至少1秒之后,同步任务都执行完了,再去执行。这时候的setTimeout回调执行的时机就会超过2秒,也就是至少3秒
JS调用栈
JS调用栈 采用的是后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空
同步任务和异步任务
Javascript单线程任务 被分为 同步任务 和 异步任务
同步任务 会在调用栈中按照顺序等待主线程依次执行
异步任务 会在异步任务有了结果后,将注册的回调函数放入 任务队列 中等待主线程空闲的时候(调用栈被清空),被读取到栈内等待主线程的执行
任务队列 Task Queue,即 队列,是一种 先进先出 的一种数据结构
异步任务 分为 宏任务(macrotask) 与 微任务 (microtask),不同的 API注册 的任务会依次进入自身对应的队列中,然后等待 Event Loop 将它们依次压入执行栈中执行
宏任务 和 微任务
任务队列其实不止一种,根据任务种类的不同,可以分为:
微任务(micro task)队列 和 宏任务(macro task)队列
宏任务: script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)
微任务: Promise、MutaionObserver、process.nextTick(Node.js 环境);
一次 Eventloop 循环会处理一个宏任务和所有这次循环中产生的微任务
简而言之,一次事件循环只执行处于 Macrotask 队首的任务,执行完成后,立即执行 Microtask 队列中的所有任务
示例
console.log('同步代码1');
setTimeout(() => {
console.log('setTimeout')
}, 0)
new Promise((resolve) => {
console.log('同步代码2')
resolve()
}).then(() => {
console.log('promise.then')
})
console.log('同步代码3');
输出:
"同步代码1"
"同步代码2"
"同步代码3"
"promise.then"
"setTimeout"
讲解
1、遇到第一个 console,它是同步代码,加入执行栈,执行并出栈,打印出 "同步代码1";
2、遇到 setTimeout,它是一个宏任务,加入宏任务队列;
3、遇到 new Promise 中的console,它是同步代码,加入执行栈,执行并出栈,打印出 "同步代码2";
4、遇到 Promise then,它是一个微任务,加入微任务队列;
5、遇到第三个 console,它是同步代码,加入执行栈,执行并出栈,打印出 "同步代码3";
6、此时执行栈为空,去执行微任务队列中所有任务,打印出 "promise.then";
7、执行完微任务队列中的任务,就去执行宏任务队列中的一个任务,打印出 "setTimeout"
宏任务 和 微任务 的本质区别
微任务:不需要特定的异步线程去执行,没有明确的异步任务去执行,只有回调
宏任务:需要特定的异步线程去执行,有明确的异步任务去执行,有回调
总结描述
执行栈在执行完同步任务后,查看执行栈是否为空,如果执行栈为空,就会去检查 微任务(microTask)队列 是否为空
如果 微任务(microTask) 队列不为空 就一次性执行完所有微任务, 如果 微任务(microTask) 队列为空的话,就执行Task(宏任务)
每次单个 宏任务 执行完毕后,检查微任务(microTask)队列是否为空,如果不为空的话,会按照先入先出的规则全部执行完 微任务(microTask)后,设置 微任务(microTask)队列为null,然后再执行宏任务,如此循环
所有任务都在主线程上执行,形成一个执行栈
主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件
一旦 "执行栈" 中的所有同步任务执行完毕,系统就会读取"任务队列"
那些对应的异步任务,结束等待状态,进入执行栈并开始执行
主线程不断重复上面的第三步
Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:
1、执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
2、检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
3、更新render(每一次事件循环,浏览器都可能会去更新渲染)
4、重复以上步骤
宏任务 > 所有微任务 > 宏任务
示例
setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
}).then(function(val){
console.log(val);
})
console.log(4);
=> 2,4,3,1
解析:
1、先执行script同步代码
先执行new Promise中的 console.log(2), then后面 的不执行属于微任务
然后执行console.log(4)
2、执行完script宏任务后,执行微任务,console.log(3),没有其他微任务了
3、执行另一个宏任务,定时器,console.log(1)
标签:function,触发,console,任务,循环,事件,第十五,event
From: https://www.cnblogs.com/caix-1987/p/17291205.html