介绍Javascript中对Event处理,包括执行顺序、自定义事件、事件触发

一般通过如下的方法对元素添加监听事件

target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);

当第3个参数是object时,可以有下面的参数

  • capture Boolean值,表明监听函数在捕获阶段触发
  • once Boolean值,表明监听函数在触发一次之后,就会自动从监听事件的列表中移除(相当于removeEventListener)
  • passive Boolean值,表明在监听函数内部不会使用 preventDefault() 来取消浏览器的默认行为。即使调用 preventDefault() 也没有任何效果

关于 passive 的介绍可以看 这篇文章

执行顺序

首先,现在的浏览器基本都支持 冒泡+捕获 的事件流模型,这里就不在多加介绍

执行顺序按照下面的规则(个人摸索出来的规则)

对事件对象来说: window > document > body > 父类元素 > 事件目标

对注册类型来说: 内联事件 优先于 通过代码注册的事件(内联事件也就是写在html元素标签属性上的事件,如 <a οnclick='...'></a>)

对事件触发阶段来说: 捕获 优先于 冒泡

对注册事件的顺序来说: 先注册的先调用

通过实际例子检验一下:

<div id='div1'>
	<div id='div2'>
		<div id='div3'>
			<button type='button' onclick='console.log("inline onclick")'>Test</button>
			<p>11111111111111</p>
		</div>
	</div>
</div>

function x(e){
	//用 eventPhase 来检测当前事件的处理阶段
	return  e.eventPhase==3 ? 'bubbles':'capture '
}

window.addEventListener('click',(e)=>{
	console.log('window click 0 in '+ x(e),e)
});

window.addEventListener('click',(e)=>{
	console.log('window click 1 in '+ x(e),e)
});

window.addEventListener('click',(e)=>{
	console.log('window click 2 in '+ x(e),e)
},true);

document.addEventListener('click',(e)=>{
	console.log('document click in ' + x(e) ,e)
})

document.addEventListener('click',(e)=>{
	console.log('document click in ' + x(e) ,e)
},true)

document.querySelector('#div2').addEventListener('click',(e)=>{
	console.log('----start------')
	console.log('event in ' + x(e),e)
	console.log('target',e.target)
	console.log('current target',e.currentTarget)
	console.log('----end------')
})

document.querySelector('button').addEventListener('click',(e)=>{
	console.log('----start------')
	console.log('event in ' + x(e),e)
	console.log('target',e.target)
	console.log('current target',e.currentTarget)
	console.log('----end------')
},true)

点击按钮,我们可以看一下控制台是如何输出的

一般是使用下方两个方法中断事件流的传播(注意,阻止事件传播不是销毁event对象,event依然会返回给浏览器)

event.stopPropagation() // 阻止event往其他事件目标传播,但是当前事件目标其他的监听函数会依次调用
event.stopImmediatePropagation() // 立即阻止event传播,不会调用其他的监听函数

阻止事件执行浏览器的默认行为(调用之后无法恢复)

event.preventDefault()

有些事件的触发机制是连续性的,比如 click 事件需要 mousedownmouseup 这两个前置事件的触发(鼠标按键按下然后弹起才算是一个完整的点击),如果在mouseup里用event.preventDefault() 阻止mouseup的默认行为,click 事件也因此无法触发。 类似的有 keypress 需要 keydownkeyup,还有其他。

这里有一个简单的应用:比如一个网站使用这样的代码document.addEventListener('copy',(e)=>{e.preventDefault()}) 让用户不能复制页面上的内容。 要突破复制限制,一般的解决方法是用 removeEventListener,可是这里行不通,因为监听函数是一个匿名函数,而你又无法得到这个函数的引用。 替代的方案是window.addEventListener('copy',(e)=>{e.stopImmediatePropagation()})

这里利用了监听函数执行的优先顺序,提前终止了事件的传播,让网站阻止复制的代码无法执行。

自定义事件

Event 接口代表了DOM中所有事件,是所有事件的父类。在DOM中事件的种类非常多(链接),他们的初始化参数也比较复杂,不可能一一列举,这里只介绍几种用代码的方式自动义事件的例子(不推荐使用 document.createEvent的方式构建事件)

Event 构造函数 (初始化参数请参考文档
event = new Event(typeArg, eventInit);

typeArg表示这个事件的名称,可以按照自己的需求定义。如果名称定义为浏览器内置事件的名称,这个事件是没有实际效果的,因为这些事件都有属于他们自己的构造函数,如 click 对应 new MouseEvent

eventInit对象有3个参数

  • bubbles Boolean值,表明事件是否需要冒泡阶段
  • cancelable Boolean值,表明事件是否可以取消
  • composed Boolean值,跟 Shadow DOM 有关,暂时不了解作用

eventInit只有3个参数,基本没什么用处,也不能传递数据,所以通过Event构造函数创建的事件只有简单的通知作用,告诉你有某个事件被触发了,然后就没了。

MouseEvent 构造函数(初始化参数请参考文档
 event = new MouseEvent(typeArg, mouseEventInit);

mouseEventInit 可用的参数比较多,选择几个重要的说明一下

mouseEventInit = {
	button: 0, // 鼠标事件的按钮,0是左键,1是中键,2是右键
	ctrlKey: false,
	shiftKey: false,
	altKey: false, // 对应于键盘上的三个控制键是否被按下
	screenX: 0,
	screenY: 0, // 对应事件触发时鼠标在用户屏幕上的坐标
	clientX: 0,
	clientY:0, // 对应事件触发时鼠标在用户浏览器界面上的坐标
}

创建一个鼠标事件可以这样

var event = new MouseEvent('click',{}) // 对,什么都不要,因为参数都有默认值

或者正规一点

var event = new MouseEvent('click', {
	button: 1,
	view: window,
	bubbles: true,
	cancelable: true
});
CustomEvent 构造函数
event = new CustomEvent(typeArg, customEventInit);

CustomEvent顾名思义,就是自定义事件,它可以让我们把数据放在事件对象里面,并且随着事件的传播而传播(终于有一个实用的了),而上面两个事件一般都由浏览器根据用户的行为触发

var event = new CustomEvent('cat', {
	detail: { // 数据放在 detail 下面,支持任何类型的数据,比如这里的数据类型是一个对象
		data: 'balabalalalala',
		getMySalary: function(){
			return 20000;
		}
	}
});

要创建浏览器自带事件,并用代码来模拟事件的触发,需要选择正确构造函数和正确的构造参数来创建事件。这些参数都在 MDN 上有详细的介绍,想对事件深入理解的话不妨认真阅读。

事件触发

我个人了解到的总共有这3种触发方式

  • 用户事件触发,浏览器自己触发事件
  • 因为事件的传播而被动触发,比如事件委托
  • 用 element.dispatchEvent(event) 或 用 click()、focus()、blur()等 代码的方式主动触发

这三种方法的触发是相互影响的,并不能完全独立开来。

“原生”事件如浏览器自己触发事件在执行监听函数时是异步执行的,但是 element.dispatchEvent(event) 是同步执行的,也就是说,dispatchEvent调用后,当前的JS运行线程会阻塞在这里,直到所有的监听函数被执行并结束。

鉴别是浏览器派发的事件还是通过JS代码的方式手动触发的事件,只需检测 event里的isTrused 属性,为true就是浏览器触发的,false则是代码触发的,这个属性无法用初始化参数进行初始化

转载于:https://my.oschina.net/reter/blog/1922661

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值