汇总js事件相关及事件处理模型

JavaScript019

汇总js事件相关及事件处理模型,第1张

js事件是为了实现用户交互,比如当用户鼠标点击或者键盘输入时,浏览器会监听截获并且通知js做出反馈执行相应的函数,实现交互。

js的事件类型有很多,我们先来大概总结一下常用的JS事件

click点击事件、mousedown鼠标按下事件、mousemove鼠标移动事件、mouseup鼠标抬起事件

contextmenu右键出菜单事件、mouseenter/mouseover鼠标进入事件、mouserleave/mouseout鼠标离开事件

可通过事件对象的button属性来区分是左键\滚轮\右键,分别对应值 0 / 1 / 2

DOM3规定:click事件只能监听左键;只能通过mousedown和mouseup来判断鼠标键

keydown某键被按下时、keyup被松开时、keypress按下并且松开时

触发顺序:先 keydown,然后keypress,最后keyup

keydown和keypress的区别:

input:文本变化时触发

change:聚焦或失去焦点时判断状态是否改变,发生改变是触发change事件

focus:聚焦时触发

blur:失去焦点时触发

利用focus和blur可模拟placeholder

scroll:滚动条滚动时

load:页面加载完触发

abort:图像的加载失败

dblclick:双击事件

error:当加载图像和文档时发生错误

resize:窗口或者框架被重新调整大小

select:文本被选中时

reset:点击重置按钮时

submit:点击提交按钮时

接着看如何给DOM元素绑定事件处理函数

兼容性很好

但是根据js的特点,这种赋值的方式肯定会被后面的值覆盖。因此这种方式绑定事件处理函数规则是:同一个元素同一个事件只能绑定一个处理函数。等同于第一种写在行间。

IE8及以下不兼容

通过addEventListener绑定的方式,同个元素的同一个事件可以绑定多个处理函数,不会被覆盖。

attachEvent跟addEventListener 基本一致,也是同一个元素的同一个事件可以绑定多个处理函数,不会被覆盖。不同的是attachEvent可以绑定的函数是可以重复的,即即使绑定同一个函数都不会不覆盖。

以上几种事件绑定方式里面的this指向有点区别:

也比较好理解,IE独有的特殊一点指向window,其他都指向dom元素本身

封装一个兼容性的方法,用于绑定事件:

有的时候我们希望解除事件处理函数,那怎么办呢?

其实解除事件处理函数也对应有办法方法:

1. ele.onxxx = false / ' ' / null

2. ele.removeEventListener(type,fnName,false)

3. ele.attachEvent('on' + type,fnName)

值得注意的是:若干绑定的事件处理函数是匿名函数,则无法解除绑定!

不知道有没有发现,在上面绑定事件处理函数的时候,处理函数有个参数e或者叫event,其实是一个事件对象

事件对象就是处理函数里面的一个参数,说白了就是浏览器打包好的一个对象自动传入到处理函数的第一个参数中。

为了兼容IE一般这么写: e = e || window.event

事件对象会有个属性target,这个target叫事件源对象,记录可事件具体在谁身上触发的那个源头

同样IE上事件源对象是e.srcElement,谷歌两个都有

所以为了兼容IE一般获取事件源对象是这么来写:

当浏览器发展到第四代时(IE4及Netscape4),浏览器开发团队遇到了一个很有意思的问题:页面的哪一部分会拥有某个特定的事件?想象画在一张纸上的一组同心圆。如果把手指放在圆心上,那么手指指向的不是一个圆,而是纸上的所有圆

两家公司的浏览器开发团队在看待浏览器事件方面还是一致的。如果单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上,甚至也单击了整个页面

但有意思的是,IE和Netscape开发团队居然提出了差不多是完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流。事件冒泡和事件捕获称为两种事件处理模型

IE的事件流叫做事件冒泡(event bubbling), 事件冒泡是结构上(非视觉上)嵌套的函数存在事件冒泡功能,即同一事件自子元素冒泡向父元素(自底向上)

[注意]所有现代浏览器都支持事件冒泡

但在具体实现在还是有一些差别。IE9、Firefox、Chrome、Safari将事件一直冒泡到window对象

而事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前就捕获它

即事件捕获是指: 结构上(非视觉上)嵌套的元素存在事件捕获功能,即同一事件自父元素冒泡向子元素(自顶向下)

注意IE上没有事件捕获,Chrome和新版本的Firefox等都实现了

一个dom元素的一个事件类型绑定的一个处理函数只能存在一种事件模型,要么事件冒泡要么事件捕获。正常的通过addEventListener(type,fn,false)绑定事件时,最后一个参数默认是false表示的是,事件冒泡模型。如果改成true,立即变成事件捕获模型

如果一个dom元素的一个事件类型绑定了两个处理函数,两个函数的事件处理模型一个是事件冒泡一个是事件捕获,触发顺序是先捕获,后冒泡。

focus、blur、change、submit、reset、select等事件类型不冒泡

可以 利用事件冒泡和事件源对象可以叫事件委托给父元素

利用事件冒泡和事件源对象进行处理

优点:

性能好,不需要循环所有元素一个个绑定事件

灵活,当有其他新的子元素时,不需要重新绑定事件。

有的时候,我们不希望有冒泡功能,那我们怎么取消事件冒泡呢?

封装一个都好使的取消冒泡函数

有的时候我们需要阻止一些浏览器默认的事件

比如:表单提交、a链接跳转、右键菜单等

有几种方式:

参考资料:

DOM事件流的三个阶段

深入理解DOM事件机制系列第一篇——事件流

JS事件汇总

JS事件模型

事件1(上)

事件1(下)

DOM级别与DOM事件

DOM事件机制解惑

事件模型

JavaScript 事件委托详解

JavaScript 事件的学与记:stopPropagation 和 stopImmediatePropagation

event.target和event.currentTarget的区别

js怎么区分出点击的是鼠标左键还是右键?

事件处理程序的方式了:

1. 设置HTML标签属性为事件处理程序

文档元素的事件处理程序属性,其名字由“on”后面跟着事件名组成,例如:onclick、onmouseover。当然了,这种形式只能为DOM元素注册事件处理程序。实例:

<!DOCTYPE HTML>

<html>

<head>

<meta http-equiv="Content-Type" content="text/htmlcharset=utf-8"/>

<title>test</title>

<style type="text/css">

#div1{width: 300pxheight: 300pxbackground: redoverflow:hidden}

#div2{margin:50px autowidth: 200pxheight: 200pxbackground: greenoverflow:hidden}

#div3{margin:50px autowidth: 100pxheight: 100pxbackground: blue}

</style>

</head>

<body>

<div id="div1" onClick="console.log('div1')">div1

<div id="div2" oNClick="console.log('div2')">div2

<div id="div3" onclick="console.log('div3')" onclick="console.log('div3333')">div3

</div>

</div>

</div>

<script type="text/javascript">

</script>

</body>

</html>

结果(鼠标点击div3区域后):

从结果中可以看出:

①因为HTML里面不区分大小写,所以这里事件处理程序属性名大写、小写、大小混写均可,属性值就是相应事件处理程序的JavaScript代码;

②若给同一元素写多个onclick事件处理属性,浏览器只执行第一个onclick里面的代码,后面的会被忽略;

③这种形式是在事件冒泡过程中注册事件处理程序的;

2.设置JavaScript对象属性为事件处理程序

可以通过设置某一事件目标的事件处理程序属性来为其注册相应的事件处理程序。事件处理程序属性名字由“on”后面跟着事件名组成,例如:onclick、onmouseover。实例:

<!DOCTYPE HTML>

<html>

<head>

<meta http-equiv="Content-Type" content="text/htmlcharset=utf-8"/>

<title>test</title>

<style type="text/css">

#div1{width: 300pxheight: 300pxbackground: redoverflow:hidden}

#div2{margin:50px autowidth: 200pxheight: 200pxbackground: greenoverflow:hidden}

#div3{margin:50px autowidth: 100pxheight: 100pxbackground: blue}

</style>

</head>

<body>

<div id="div1">div1

<div id="div2">div2

<div id="div3">div3

</div>

</div>

</div>

<script type="text/javascript">

var div1 = document.getElementById('div1')

var div2 = document.getElementById('div2')

var div3 = document.getElementById('div3')

div1.onclick = function(){

console.log('div1')

}

div2.onclick = function(){

console.log('div2')

}

div3.onclick = function(){

console.log('div3')

}

div1.onclick = function(){

console.log('div11111')

}

div1.onClick = function(){

console.log('DIV11111')

}

</script>

</body>

</html>

结果(鼠标点击div3区域后):

从结果中可以看出:

①因为JavaScript是严格区分大小写的,所以,这种形式下属性名只能按规定小写;

②若给同一元素对象写多个onclick事件处理属性,后面写的会覆盖前面的(ps:这就是在修改一个对象属性的值,属性的值是唯一确定的);

③这种形式也是在事件冒泡过程中注册事件处理程序的;

3.addEventListener()

前两种方式出现在Web初期,众多浏览器都有实现。而addEventListener()方法是标准事件模型中定义的。任何能成为事件目标的对象——这些对象包括Window对象、Document对象和所有文档元素等——都定义了一个名叫addEventListener()的方法,使用这个方法可以为事件目标注册事件处理程序。addEventListener()接受三个参数:第一个参数是要注册处理程序的事件类型,其值是字符串,但并不包括前缀“on”;第二个参数是指当指定类型的事件发生时应该调用的函数;第三个参数是布尔值,其可以忽略(某些旧的浏览器上不能忽略这个参数),默认值为false。这种情况是在事件冒泡过程中注册事件处理程序。当其为true时,就是在事件捕获过程中注册事件处理程序。实例:

<!DOCTYPE HTML>

<html>

<head>

<meta http-equiv="Content-Type" content="text/htmlcharset=utf-8"/>

<title>test</title>

<style type="text/css">

#div1{width: 300pxheight: 300pxbackground: redoverflow:hidden}

#div2{margin:50px autowidth: 200pxheight: 200pxbackground: greenoverflow:hidden}

#div3{margin:50px autowidth: 100pxheight: 100pxbackground: blue}

</style>

</head>

<body>

<div id="div1">div1

<div id="div2">div2

<div id="div3">div3

</div>

</div>

</div>

<script type="text/javascript">

var div1 = document.getElementById('div1')

var div2 = document.getElementById('div2')

var div3 = document.getElementById('div3')

div1.addEventListener('click', function(){ console.log('div1-bubble')}, false)

div2.addEventListener('click', function(){ console.log('div2-bubble')}, false)

div3.addEventListener('click', function(){ console.log('div3-bubble')}, false)

div3.addEventListener('click', function(){ console.log('div3-bubble222')}, false)

div1.addEventListener('click', function(){ console.log('div1-capturing')}, true)

div2.addEventListener('click', function(){ console.log('div2-capturing')}, true)

div3.addEventListener('click', function(){ console.log('div3-capturing')}, true)

</script>

</body>

</html>

结果(鼠标点击div3区域后):

从结果中可以看出:

①addEventListener()第三个参数的作用正如上面所说;

②通过addEventListener()方法给同一对象注册多个同类型的事件,并不会发生忽略或覆盖,而是会按顺序依次执行;

相对addEventListener()的是removeEventListener()方法,它同样有三个参数,前两个参数自然跟addEventListener()的意义一样,而第三个参数也只需跟相应的addEventListener()的第三个参数保持一致即可,同样可以省略,默认值为false。它表示从对象中删除某个事件处理函数。实例:

div1.addEventListener('click', div1BubbleFun, false)

div1.removeEventListener('click', div1BubbleFun, false)

function div1BubbleFun(){

console.log('div1-bubble')

}

4.attachEvent()

但是,IE8以及其之前版本的浏览器并不支持addEventListener()和removeEventListener()。相应的,IE定义了类似的方法attachEvent()和detachEvent()。因为IE8以及其之前版本浏览器也不支持事件捕获,所以attachEvent()并不能注册捕获过程中的事件处理函数,因此attachEvent()和detachEvent()要求只有两个参数:事件类型和事件处理函数。而且,它们的第一个参数使用了带“on”前缀的事件处理程序属性名。实例:

var div1 = document.getElementById('div1')

div1.attachEvent('onclick', div1BubbleFun)

function div1BubbleFun(){

console.log('div1-bubble')

}

相应的,从对象上删除事件处理程序函数使用detachEvent()。例如:

div1.detachEvent('onclick', div1BubbleFun)

到此为止,我们已经说了浏览器中事件传播机制以及各种注册事件处理程序的方法。下面我们就再说说事件处理程序调用时的一些问题吧!

二.事件处理程序的调用

1.事件处理程序的参数:正如前面所说,通常事件对象作为参数传递给事件处理函数,但IE8以及其之前版本的浏览器中全局变量event才是事件对象。所以,我们在写相关代码时应该注意兼容性问题。实例(给页面上id为div1的元素添加点击事件,当点击该元素时在控制台输出事件类型和被点击元素本身):

<!DOCTYPE HTML>

<html>

<head>

<meta http-equiv="Content-Type" content="text/htmlcharset=utf-8"/>

<title>test</title>

<style type="text/css">

#div1{width: 300pxheight: 300pxbackground: redoverflow: hidden}

</style>

</head>

<body>

<div id="div1">div1</div>

<script type="text/javascript">

var div1 = document.getElementById('div1')

if(div1.addEventListener){

div1.addEventListener('click', div1Fun, false)

}else if(div1.attachEvent){

div1.attachEvent('onclick', div1Fun)

}

function div1Fun(event){

event = event || window.event

var target = event.target || event.srcElement

console.log(event.type)

console.log(target)

}

</script>

</body>

</html>

2.事件处理程序的运行环境:关于事件处理程序的运行环境,也就是在事件处理程序中调用上下文(this值)的指向问题,可以看下面四个实例。

实例一:

<!DOCTYPE HTML>

<html>

<head>

<meta http-equiv="Content-Type" content="text/htmlcharset=utf-8"/>

<title>test</title>

<style type="text/css">

#div1{width: 300pxheight: 300pxbackground: redoverflow: hidden}

</style>

</head>

<body>

<div id="div1" onclick="console.log('html:')console.log(this)">div1</div>

<script type="text/javascript">

</script>

</body>

</html>

结果一:

从结果可以看出:

①第一种方法事件处理程序中this指向这个元素本身;

实例二:

<!DOCTYPE HTML>

<html>

<head>

<meta http-equiv="Content-Type" content="text/htmlcharset=utf-8"/>

<title>test</title>

<style type="text/css">

#div1{width: 300pxheight: 300pxbackground: redoverflow: hidden}

</style>

</head>

<body>

<div id="div1" onclick="console.log('html:')console.log(this)">div1</div>

<script type="text/javascript">

var div1 = document.getElementById('div1')

div1.onclick = function(){

console.log('div1.onclick:')

console.log(this)

}

</script>

</body>

</html>

结果二:

从结果可以看出:

①第二种方法事件处理程序中this也指向这个元素本身;

②存在第二种方法时,它会覆盖第一种方法注册的事件处理程序;

实例三:

<!DOCTYPE HTML>

<html>

<head>

<meta http-equiv="Content-Type" content="text/htmlcharset=utf-8"/>

<title>test</title>

<style type="text/css">

#div1{width: 300pxheight: 300pxbackground: redoverflow: hidden}

</style>

</head>

<body>

<div id="div1" onclick="console.log('html:')console.log(this)">div1</div>

<script type="text/javascript">

var div1 = document.getElementById('div1')

div1.onclick = function(){

console.log('div1.onclick:')

console.log(this)

}

div1.addEventListener('click', function(){

console.log('div1.addEventListener:')

console.log(this)

}, false)

</script>

</body>

</html>

结果三:

从结果可以看出:

①第三种方法事件处理程序中this也指向这个元素本身;

②第三种方法并不会覆盖第一种或第二种方法注册的事件处理程序;

实例四:

<!DOCTYPE HTML>

<html>

<head>

<meta http-equiv="Content-Type" content="text/htmlcharset=utf-8"/>

<title>test</title>

<style type="text/css">

#div1{width: 300pxheight: 300pxbackground: redoverflow: hidden}

</style>

</head>

<body>

<div id="div1" onclick="console.log('html:')console.log(this)">div1</div>

<script type="text/javascript">

var div1 = document.getElementById('div1')

div1.onclick = function(){

console.log('div1.onclick:')

console.log(this)

}

div1.attachEvent('onclick', function(){

console.log('div1.attachEvent:')

console.log(this === window)

})

</script>

</body>

</html>

结果四:

从结果可以看出:

①第四种方法事件处理程序中this指向全局对象Window;

②第四种方法也不会覆盖第一种或第二种方法注册的事件处理程序;

3.事件处理程序的调用顺序:多个事件处理程序调用规则如下:

①通过HTML属性注册的处理程序和通过设置对象属性的处理程序一直优先调用;

②使用addEventListener()注册的处理程序按照它们的注册顺序依次调用;

③使用attachEvent()注册的处理程序可能按照任何顺序调用,所以代码不应该依赖于调用顺序;

用v-on指令或者@监听DOM事件,并在触发时运行一些JavaScript代码;

参考:JS,Vue2,介绍,与Vue3区别,MVVM设计模式,模板语法,数据绑定

Vue中的事件修饰符:

1、prevent:阻止默认事件(常用);

2、stop:阻止事件冒泡(常用);

3、once:事件只触发一次(常用);

4、capture:使用事件的捕获模式;

5、self:只有event.target是当前操作的元素时才触发事件;

6、passive:事件的默认行为立即执行,无需等待事件回调执行完毕;

Vue中常用的按键别名:

回车 =>enter、删除 =>delete (捕获“删除”和“退格”键)、退出 =>esc、空格 =>space、换行 =>tab (特殊,必须配合keydown去使用)、上 =>up、下 =>down、左 =>left、右 =>right等。

代码案例

模板内的表达式是非常便利的,用于简单的运算。当其过长或逻辑过于复杂时,会难以维护,因此,当遇到复杂的逻辑时应该使用计算属性,用computed计算属性。

代码案例

通过watch来响应数据的变化。

代码案例

class样式,写法:class="xxx" ,其中xxx可以是字符串、对象、数组。

1、字符串写法适用于:类名不确定,要动态获取。

2、对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。

3、数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

style样式,写法::style="{fontSize: xxx}",其中xxx是动态值;:style="[a,b]"其中a、b是样式对象。

代码案例