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

通常经过以下的方法对元素添加监听事件html

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 onclick='...'></a>)spa

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

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

经过实际例子检验一下:htm

<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则是代码触发的,这个属性没法用初始化参数进行初始化

相关文章
相关标签/搜索