JavaScript之事件机制

本文详细介绍了JavaScript的事件机制,包括事件机制环节、事件流(冒泡和捕获)、事件处理程序(绑定与解绑)以及事件委托。阐述了不同事件绑定和解绑方式的特点、兼容性及this指向问题,还说明了事件委托的原理和操作步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaScript之事件机制

转:事件机制

一、事件机制环节

事件机制分为四个环节:

  1. 事件流(冒泡、捕获)
  2. 事件处理程序
  3. 事件对象
  4. 事件委托

二、事件流

事件流:指从页面中接收事件的顺序,有冒泡流和捕获流。
当页面中发生某种事件(比如鼠标点击,鼠标滑过等)时,毫无疑问子元素和父元素都会接收到该事件,可具体顺序是怎样的呢?冒泡和捕获则描述了两种不同的顺序。

那么冒泡和捕获十一什么样的形式发生的呢?接下来,一张图,帮助你来理解:
在这里插入图片描述

你想一下,js的语言特点是什么就知道了,其中一个特点就是事件机制。这样你就知道,这个事件的重要性了,这也是面试老生常谈的话题。
事件是每一个浏览器本来就有的,我们只是给相应的事件添加了一个回调函数。这样在触发一个函数之后,相应的会进行调用回调函数。那么该有疑问了,事件是用来做什么的,那么用一个小demo来感受一下,这个事件是用来做什么用的。

div.addEventListener('mousedown', function (e){
var disX = e.clientX - parseInt(getStyle(this, 'left')),
	disY = e.clientY - parseInt(getStyle(this, 'top'));//这是获取鼠标点跟元素最左边和最上边的距离
document.addEventListener('mousemove', mouseMove, false);//用document是有原因的,加入你拖动的比较快,会脱离这个元素,如果你用元素的话,这个元素会停止。
	div.addEventListener('mouseup', function (e) {
		document.removeEventListener('mousemove', mouseMove, false);
	}, false);
}, false);
 
function mouseMove(e) {
      div.style.left = e.clentX - disX + 'px';//
      div.style.top = e.clientY - disY + 'px';//必须要减去diX、diY,这样这个鼠标点才会在你指定的地方。
 
}

这只是一个简单的拖拽的例子。里面用到了的事件有onmousedown、onmousemove、onmouseup.这三个兄弟基本是连体的,要用一起用。

三、事件处理程序

事件的绑定

  1. 句柄方式
var div = document.getElementsByTagName('div')[0];
div.onclick = function (e) {
      console.log('a');
}
div.onclick = function(){
       console.log("b")
 }

后面的这个函数就叫做事件处理函数,也称回调函数,当我们点击事件触发的时候,就会执行后面的处理函数。

事件可以持续监听,并不是执行完一次就失效,因此这个事件监听部分并不属于js引擎,因为js引擎是单线程的,事件监听属于内核的其他模块部分,一旦事件触发,事件监听就会把处理函数放入执行队列,等待js引擎来执行。

虽然句柄方式的兼容性很好,但是一个元素的一种事件只能绑定一个函数。

如果用这种方式,绑定多个事件的话,前面的事件就会被覆盖掉的,所以还是相当于就绑定了一个事件。虽然说有好处,但是缺点要比好大的多。

这种方式也可以写在行间样式上面:

<div onclick="console.log('a')"></div>

或者里面写一个函数名,在js中把这个函数给定义出来,也相当于一个执行队列,等待着被调用。

  1. ele.addEventListener(type, handle, false)方法
div.addEventListener('click', function(e) {
      console.log('a');
}, false);

这里面有三个参数,第一个参数是事件类型,第二个参数是处理函数,第三个参数是是否捕获。

当第三个对象为true时,则就变成了捕获。

处理函数可以直接在addEventListener方法里面写一个匿名函数,也可以在外面写一个命名函数,然后在放法里面写函数的引用。

这种方法更加通用常见,而且一种事件可以绑定多个函数,但是同一个处理函数只能绑定一次

function test1 () {
      console.log('a');
}
function test2() {
      console.log('a');
}
div.addEventListener('click', test1, false);
div.addEventListener('click', test2, false);

这样的话,这两个函数都会执行,也不会被覆盖的。

当然它也是唯一一个可以进行捕获的事件绑定的方式。但是兼容性的问题,IE9以下是不兼容的。

  1. ele.attachEvent(‘on’ + type, handle)方式

这种方式是IE独有的一种方式,同样也是可以进行绑定多个回调函数的。

div.attachEvent('onclick', function (){
      console.log('a');
});

基本和addEventListener差不多,但是有一点区别是,当同一个函数绑定多次的时候,addEventListener是只执行一次,但是attachEvent会绑定几次执行几次。

function test () {
     console.log('a');
}
div.attachEvent('onclick', test);
div.attachEvent('onclick', test);

这样回打印两个a。每一种方式还是有一点不同的,所以那一种我们都必须得学习。

接下来,用的比较的多的,也是特别使用的是,在这三个时间处理中的this指向是很重要的。

  • 句柄绑定方式中,函数里面的this指向元素本身。

  • addEventListener方式中,函数里面的this也是指向元素本身。

  • attachEvent中,函数里面的this指向的是window而不是元素本身,这算是IE的一个BUG。针对这种情况,我们就需要把函数提取出来,然后在attachEvent的时候用call来改变函数内部this的指向。

div.attachEvent('onclick', function () {
      test.call(div);
}, false);

有了这些作为知识点,那样的话,我们就可以封装一个函数,来做一下兼容处理:

	function attachEvent(ele, type, handle) {
		if (ele.addEventListener) {
			ele.addEventListener(type, handle, false);
		}else if (ele.attachEvent) {
			ele.attachEvent('on' + type, function () {
				handle.call(ele);
			});
		}else {
			ele['on' + type] = handle;
		}
	}

这样还是存在一个小问题,因为第二种方法引用了一个匿名函数,这样我们就解除不了绑定了,所以,我们得把这个匿名函数变成“有名”的不就行了吗。接下来进行优化:

 function attachevent(ele, type, handle){
            if(ele.addEventListener){
                ele.addEventListener(type, handle, false)
            }
            else if(ele.attachEvent){
                ele['similate' + type + handle] = handle;//把这个函数给保存起来
                ele[type + handle] = function(){//给匿名函数一个名字,这样就能找到
                    ele['similate' + type + handle].call(ele);//改变一下this的指向
                }
                ele.attachEvent('on' + type,ele[type + handle])//这样的绑定就可以解除了
            }
            else{
                ele['on' + type] = handle;
            }
        }

这里专门处理了IE方法中的匿名函数问题,我们用元素自身的一个属性来保存了这个处理函数。这样才是最完整的一个方法了,也是经常用的一个方法了。

有绑定,就会有解除绑定的

事件的解绑

  1. 句柄方式

ele.οnclick=null

这样很简单的就可以解除绑定的事件处理函数了。

  1. ele.removeEventListener(type, handle, false)

针对的addEventListener的解除绑定。

但是这里要注意,只有命名函数才可以解除绑定,当绑定的函数是匿名函数的时候,是没有办法解除绑定的。

div.addEventListener('click', function (){console.log('a');, false);
div.removeEventListener('click', function (){console.log('a');, false);

这种形式是解除不了绑定的,刚才封装方法的时候,就特别进行优化的。

  1. ele.detachEvent(‘on’ + type, handle)

针对IE的attachEvent的解除绑定。

如果不想改变this指向问题,这就无所谓了,但是想要改变this指向,就一定要注意一下了。

封装解除绑定的方法:

function remvoeEvent(ele, type, handle) {
      if(ele.removeEventListener) {
            ele.removeEventListener(type, handle, false);
      }else if (ele.detachEvent) {
            ele.detachEvent('on' + type, handle);
      }else {
            ele['on' + type] = null;
      }
}

四、事件委托

事件委托是指将事件绑定在父元素上,然后采用事件冒泡的方法,当事件流达到父元素时,可以通过target获取真正触发的当前元素,并执行绑定在父元素上的方法。

这样做可以省去一个个给子元素绑定事件。

一般的步骤是:

①确定需要监听时间的父元素,我现在一般需要画出DOM树

②给父元素添加addEventListener(‘event’, function)

③通过父元素.target获取实际被操作的元素,在函数中进行处理

通俗点说,就是先找到一个比较高的公共元素节点,给它绑定事件,然后通过event.target知道是哪个子节点触发了事件,找到了触发节点,就可以对它进行操作(删除它,删除它的父节点等)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值