js中事件绑定3种方法以及事件委托(js事件菜鸟教程)

事件

简述事件

  • 事件起始于IE3,作为一种分担服务器运算负载的一种手段。用于键盘、鼠标等工具对于网页的交互!事件对于不同浏览器来说,有不同的标准,尤其是IE、Chrome两大巨头浏览器上,虽然现如今Chrome已经占据大部分市场,但是对于IE8及以上的兼容也是个不小的问题。

事件类型

  • UI事件:用户与页面上的元素交互时触发;
  • 焦点事件:当元素获取失去焦点是触发;
  • 鼠标事件:当鼠标执行点击、移入移出或悬停等事件是触发;
  • 滚轮事件:当鼠标、触摸板或其他设备滚动时触发滚动条事件;
  • 文本事件:在文本框内输入文字时触发;
  • 键盘事件:当键盘按下、抬起或点击时触发;
  • 变动事件:当问文档结构发生变化时触发。

UI事件

  • load事件
  • 当页面完全加载后再window上面触发,当所有框架都加载完成时,在框架集上面触发,当图片都加载完成时在img标签上面触发,或者当嵌入的内容加载完成时在object上触发。
  • unload事件
  • 和load事件恰好相反,除img标签外,其他的框架和嵌入内容卸载完毕后在对应的级别上触发。
  • abort事件
  • 在用户停止下载过程时,如果嵌入的内容没有加载完毕,在object元素上线触发。
  • error事件
  • 当js发生语法错误时在window上触发,当发生加载图像无法成功时在img标签上触发,当嵌入的内容无法加载时在object元素上触发,或当一个或多个框架无法加载完成时在框架集上面触发。
  • select事件
  • 当用户选择文本框内的文字时触发(input或textarea)。
  • resize事件
  • 当窗口或者框架的大小变化时在window或框架上面触发。
  • scroll事件
  • 当用户滚动带有滚动条的元素中的内容时,在该元素上面触发。body上就是网页自带的滚动条。

对于文档嵌入内容不是很清楚的同学可以去看一下HTML5权威指南的第15章节内容。

焦点事件

  • blur事件、focusout
  • 输入框失去焦点事件,css有相同伪类选择器,但是不够灵活!
  • focusin事件、focus
  • 输入框获取焦点是触发,同样,css有相同的伪类事件。输入框如果使用 click同样能获取焦点,但是有点牛头马嘴的意思。
  • focus和blur事件不支持冒泡,如果不想文档事件冒泡则使用这两个事件更好,不用屏蔽事件流。

鼠标与滚轮事件

  • mousedown、mouseup事件
  • 鼠标左键按下、抬起事件,相继触发这两个事件后还会触发click事件。
  • click事件、dbclick事件
  • 鼠标单击、双击事件
  • mouseleave、mouseout事件
  • 都是鼠标脱离当前区域触发,不同的是mouseleave不冒泡。
  • mouseenter、mouseover事件
  • 鼠标进入、悬停在当前区域时触发,mouseenter事件不冒泡。
  • mousemove事件
  • 当鼠标在当前区域移动时重复触发!
  • mousewheel事件
  • 滚轮事件在鼠标中间滚动时触发,同时包含一个wheelData属性,它是120的整数倍数,滚轮向上滚动一个单位时wheelDate+120,向下滚动时-120,。

键盘与文本事件

  • keydown:当用户按下键盘等按键设备上的按钮时触发,按住不放会重复触发。
  • keyup、keypress:按键按下后释放时触发。两个事件都是同时触发keyCode的值,但是keypress会输出input框的上一个值,而keyup会立即输出当前input的值。如下:

<body>

<input type="text" >

</body>

<script>

/* 虽然id可以直接拿来使用,但是并不提倡,这不符合规范! */

test.onkeypress=function(event){ //event是系统默认传递的事件对象

console.log(event.keyCode); //输出键码

console.log(test.value); //输出input值

};

</script>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在我依次按下数字键1、2、3、4、5、6时控制台是如下效果:

index.html:15 49

index.html:16

index.html:15 50

index.html:16 1

index.html:15 51

index.html:16 12

index.html:15 52

index.html:16 123

index.html:15 53

index.html:16 1234

index.html:15 54

index.html:16 12345

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

但是如果是keyup的事件呢:

<body>

<input type="text" >

</body>

<script>

/*虽然id可以直接拿来使用,但是并不提倡,这不符合规范!*/

test.onkeyup=function(event){

console.log(event.keyCode);

console.log(test.value);

};

</script>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在我依次按下数字键1、2、3、4、5、6时控制台是如下效果:

index.html:15 49

index.html:16 1

index.html:15 50

index.html:16 12

index.html:15 51

index.html:16 123

index.html:15 52

index.html:16 1234

index.html:15 53

index.html:16 12345

index.html:15 54

index.html:16 123456

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

事件流

  • 事件流描述的是事件在网页文档中的接收顺序,它描述着事件发生时,事件是顺着文档流向上还是向下!
  • 但是奇趣的是,IE和Netscape开发团队提出了两个完全相反的事件流概念!那就是事件冒泡和事件捕获!

根据网页文档的DOM级别规定层级结构:

文档结构documenthtmlbodyelement

事件冒泡

假定事件在element上触发,然后事件会跟着下图一次冒泡

graph LR

Element-->body

body-->html

html-->document

  • 1
  • 2
  • 3
  • 4

事件是默认冒泡的,在IE9,Opera9.5及其他浏览器各版本及更高版本都是支持事件流的,但是上述明确浏览器的更低版本则不支持。

<body>

<div ></div>

</body>

<script>

/* 虽然id可以直接拿来使用,但是并不提倡,这不符合规范! */

window.onclick = function () {

console.log(this);

};

document.onclick = function () {

console.log(this);

};

document.body.parentNode.onclick = function () {

console.log(this);

};

document.body.onclick = function () {

console.log(this);

};

testElement.onclick = function () {

console.log(this);

};

</script>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

控制台输出的结果:

index.html:29 <div ></div>

index.html:26 <body>…</body>

index.html:23 <html lang="en"><head>…</head><body>…</body></html>

index.html:20 #document

index.html:17 Window{postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window,…}

  • 1
  • 2
  • 3
  • 4
  • 5

如果我们把this全价格toString方法改为字符串!

index.html:29 [object HTMLDivElement]

index.html:26 [object HTMLBodyElement]

index.html:23 [object HTMLHtmlElement]

index.html:20 [object HTMLDocument]

index.html:17 [object Window]

  • 1
  • 2
  • 3
  • 4
  • 5

事件捕获

同事件冒泡正好相反,它会按照事件流向下级结构传递:

graph LR

document-->html

html-->body

body-->element

  • 1
  • 2
  • 3
  • 4

而控制他们事件流传递方向的创建事件监听时的一个参数,后面我再详细解释。

创建和移除事件监听

创建事件监听addEventListener()方法

testElement.addEventListener("click",function(event) {

console.log(this);

console.log(event.type);

},false);

/*

index.html:32 <div id"testElement" ></div>

index.html:33 click

*/

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

它的第三个参数默认为false,代表默认使用事件冒泡规则,同时事件会根据冒泡规则一次向上传递,如果改为true则事件采用事件捕获规则,根据文档结构和元素关系进行事件的向下传递。

事件的移除removeEventListener()方法

testElement.removeEventListener("click",function(event) {

console.log(this);

console.log(event.type);

},false);

  • 1
  • 2
  • 3
  • 4

但是我们要注意的地方是,事件移除程序移除的是第二参数传递进去的函数,如果是函数体,会因为指向问题使移除监听并不算成功,而我们如果传递进去的是函数名的话,则能很好的达到效果!没听懂,没关系,看下面:

testElement.addEventListener("click",function(event) { //它没有被取消掉

console.log(this);

console.log(event.type);

},false);

testElement.removeEventListener("click",function(event) { //它没有效果

console.log(this);

console.log(event.type);

},false);

/*

index.html:32 <div ></div>

index.html:33 click

*/

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

没错,remove里的没有执行,但是add里的却仍然很好的执行了!那我们来看正确的方式!

testElement.addEventListener("click", handler, false);

testElement.removeEventListener("click", handler, false);

function handler(event) {

console.log(this)

console.log(event.type);

}

/*

控制台你什么也不会看到!

*/

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

究其原因,是因为上面的创建和移除是各自创建的函数,它们看起来很像,但是根本没有丝毫”血缘”关系,它们在内存中占据了两个不同的位置,而后面引用的函数名确实代表的同一个”东西”,它们指向的是内存中的同一个位置!!!

阻止事件的冒泡或者捕获、默认行为

  • 阻止事件流的传递,stopPropagation()方法,为了方便我这里不用规范的创建监听方法了:

<body>

<div ></div>

</body>

<script>

/* 虽然id可以直接拿来使用,但是并不提倡,这不符合规范! */

window.onclick = function () {

console.log(this);

};

document.onclick = function () {

console.log(this);

};

document.body.parentNode.onclick = function () {

console.log(this);

};

document.body.onclick = function () {

console.log(this);

};

testElement.onclick = function (event) {

console.log(this);

console.log(event.type);

event.stopPropagation(); //阻止了事件冒泡

};

</script>

/*

index.html:29 <div ></div>

index.html:30 click

*/

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

如果要测试事件捕获需要用规范的创建事件监听,并使第三个参数改为true就可以了!

  • 阻止默认行为发生
  • 说到默认行为,就像a标签点击了会默认跳转到href一样,那么我们可以通过使用preventdefault()方法实现,但是前提条件是对象的属性cancelable属性必须设置为true才可以阻止该对象的默认行为,使用时同时用event对象来调用!大家可以自行测试,这里就不赘述了。

事件委托

  • 内存和性能:在一门编程语言中,给一个控件或者目标添加看似是司空见惯的事,但是却有着非比寻常的。在包含GUI编程的语言里,给每一个控件添加事件都是没有问题的,因为它编译的时候有一些特殊的事情要做,像我学过的C#/.NET中就是这样,不过js和众多桌面语言中有着很大的却别,因为js是个单线程的语言,不能像桌面语言那样new Thread()来继续做自己的事,而是必须等待!事件在整个编译运行过程中也是遵循排队等待的铁则!那么精简的事件监听程序则是一个js脚本是否性能良好的体现!

跨浏览器的事件监听程序

  • 说起来高大上,说实在的就是因为浏览器对事件监听程序创建的差异,谷歌是addEventListener(),IE 是attachEvent(),或者其他浏览器支持用onclick=handler(以点击事件为例)创建,而对于事件,谷歌可以直接用click,而IE就必须用onclick等等,为了js程序能在不同浏览器都能跑起来,所以,一个整合的跨浏览器的事件监听创建程序非常必要!毕竟还有有很多不会升级电脑和软件的朋友存在,尤其那些还在用xp系统的办公电脑啊。。。

var EventUtil = {

addHandler: function (ele, type, handler) {

if (ele.addEventListener) {

ele.addEventListener(type, handler, false);

} else if (ele.attachEvent) {

ele.attachEvent("on" + type, handler);

} else {

ele["on" + type] = handler;

}

},

removeHandler: function (ele, type, handler) {

if (ele.removeEventListener) {

ele.removeEventListener(type, handler, false);

} else if (ele.detachEvent) {

ele.detachEvent("on" + type, handler);

} else {

ele["on" + type] = null;

}

},

getEvent: function (event) {

return event ? event : window.event; //低版本IE的event对象位置不同!

},

getTarget: function (event) {

return event.target || event.srcElement;//不同浏览器有不同的调取方法

},

preventDefault: function (event) {

if (event.preventDefault) {

event.preventDefault();

} else {

event.retrunValue = false;

}

},

stopPropagation:function(event){

if(event.stopPropagation){

event.stopPropagation();

}else{

event.cancelBubble();

}

}

};

//比如创建一个键盘监听事件

EventUtil.addHandler(document, "keyup", function (event) {

event = EventUtil.getEvent(event);

var target = EventUtil.getTarget(event);

KeyUp(target, event.keyCode ? event.keyCode.toString() : "mouse");

});

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

什么是事件委托?

  • 刚才我有提到,js本就是一个简单的脚本语言,没有桌面编程语言的多线程,所以每一个对象都创建一个甚至多个事件监听是非常耗费性能的!那么我们怎么解决呢?就是事件委托,事件委托的中心思想就是,我给当前模块尽可能给最大的父级创建事件监听程序,然后通过父级对子级进行事件的分发,对子级拥有对应功能的事件进行响应!形象化就像是一个酒店的服务台,一个服务台发送一个点击事件,服务台将事件分发到需要触发该事件的对应房间,因为js的单线程机制,所有消息和事件都是在队列中排队的,所以没有“占线”这一说法!所有的事件都能正常地分发给对应目标,从而省下很多性能资源!
  • 实现以下委托:

/*

事件处理程序创建了一个键盘监听事件,通过EventUtil对象的getEvent获取当前事件,然后通过event的特有属性,

event.target获取到事件触发时的响应目标(直接获取到元素对象!),然后通过参数等调用相应需要实现的函数

*/

EventUtil.addHandler(document, "keyup", function (event) {

event = EventUtil.getEvent(event);

var target = EventUtil.getTarget(event);

KeyUp(target, event.keyCode ? event.keyCode.toString() : "mouse");

});

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 这里我把键盘事件放在document上,是为了键盘能在全局被监听到,使用时并不是对象越大越好,一般来说模块化里同一个子父级树里尽量按照更高的父级关系设置是最好的!
  • 以点击事件为例,假设我有两个不相干的盒子A和盒子B在同一个容器div盒子C里,它们各有自己的子级input框,button,div,a标签等,那么我的事件委托就可以通过对A或B的监听,将事件分发给自己的子级,事件委托对象并不是越大越好!

js中事件绑定3种方法以及事件委托(js事件菜鸟教程)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发表评论

登录后才能评论