关于事件那些事

前端 关于事件的那些事

事件是各位前端大佬们工作中避免不了的东西,大家肯定都不陌生。那么大家是否有更为细致的了解事件呢。今天我们一起来探讨探讨,有写的不好或者不对的地方,欢迎各位大佬修正。

事件,就是文档或浏览器窗口发生的一些特定的交互瞬间。也就是说html于javascript脚本之间的交互其实就是通过事件来完成的

那么,想要了解事件,有些概念就必须知道了,分别是:
事件流
事件处理程序
事件对象

1、事件流是什么

我们可以简单的理解为 事件流就是描述从页面中接收事件的顺序

2、事件流有哪几种

我们都知道有 事件冒泡事件捕获
其实最初 IE 和 Netscape所定义的事件流是不一样

IE 的事件流指的是 事件冒泡
而Netscape得事件流指得是 事件捕获

下面我们来详细看看这两种事件流

什么是事件冒泡

即 事件开始由最具体的元素逐级向上传播到最不具体的节点 也就是由内往外传播事件的过程 事件冒泡所有浏览器都支持,故比较常用

什么是事件捕获

即 事件开始由最不具体的元素逐级向下传播到最具体的节点 也就是由外往内传播事件的过程 事件捕获得目的在于 事件到达预定目标之前捕获它
事件捕获存在浏览器兼容性问题,故一般不推荐用
在这里插入图片描述

注意: 事件捕获虽然是Netscape唯一支持的事件流 但是如今大部分主流浏览器以及IE9及以上浏览器都已经支持了

3、DOM事件流

首先 最初DOM0级事件认为 事件流是只存在一个阶段得 那就是事件冒泡

后来 DOM2级事件则规定 事件流分为3个阶段 分别是 事件捕获阶段 处于目标事件阶段 事件冒泡阶段
在这里插入图片描述
注意: 在DOM事件流中,实际的目标在捕获阶段是不会接收到事件的,也就是说在捕获阶段,是不会触发目标元素上的事件处理程序的。故目标元素的事件会在 第二个阶段 处于目标阶段发生,并进行事件处理。 并且 目标元素上的事件处理会被看做第三个阶段(事件冒泡阶段) 的一部分

但是: 尽管 DOM2级事件流规定捕获阶段不会涉及到事件目标 但是,IE9及以上以及大多数主流浏览器(Safari、Chrome、Firefox 和 Opera 9.5 以及更高版本)都实现了在捕获阶段触发事件对象上的事件。故最终,目标元素上的事件实际上在捕获阶段冒泡阶段都会被触发

4、事件处理程序

什么是事件处理程序

简单理解就是 用来响应某个事件的函数 就叫做事件处理程序

事件处理程序我们可能接触过几种 比如

<body id="app">
	<div class="myDiv">
		myDiv
		<button class="btn">目标元素</button>
	</div>
</body>
<script>
	var btn = document.querySelector('.btn')
	var div = document.querySelector('.myDiv')
	var body = document.getElementById('app')

	btn.onclick = function () {
		console.log('我是目标元素')
	}
	btn.addEventListener('click', function() {
		console.log('我是目标元素')
	}, false)
	btn.attachEvent('onclick', function() {
		console.log('我是目标元素')
	})
</script>

那么 这3种直接到底有什么区别呢。下面我们来一一说明

DOM0级事件处理程序

首先,第一种

btn.onclick = function () {
	console.log('我是目标元素')
}

这种是属于DOM0级事件处理程序

特点 简捷、所有浏览器都支持 不存在兼容性问题 并且函数内部this始终指向元素本身

DMM2级事件处理程序

DOM2级事件处理程序提供了添加事件以及移除事件两个方法, 它们都接受3个参数,分别是 要处理的事件名 处理事件的函数 以及一个布尔值

btn.addEventListener('click', function() {
	console.log('我是目标元素')
}, false)
btn.removeEventListener('click', function() {
	console.log('移除事件')
}false)

注意:
1、第三个参数 默认不传时是false, 当为false时, 表示该事件处理程序在事件冒泡阶段被调用, 当为true时,表示该事件处理程序在事件捕获阶段被调用

如下:我们做同一个操作,那就是用鼠标去点击最内层的btn按钮

当第三个参数为false时

btn.addEventListener('click', function () {
    console.log('按钮 被触发了')
}, false)
div.addEventListener('click', function () {
    console.log('div 被触发了')
}, false) 
body.addEventListener('click', function () {
    console.log('body 被触发了')
}, false)

在这里插入图片描述

当第三个参数为true时

btn.addEventListener('click', function () {
    console.log('按钮 被触发了')
}, true)
div.addEventListener('click', function () {
    console.log('div 被触发了')
}, true) 
body.addEventListener('click', function () {
    console.log('body 被触发了')
}, true)

在这里插入图片描述

2、这种方法存在兼容性问题,IE9及以上、Firefox、Safari、Chrome 和 Opera等主流浏览器才支持。

3、用addEventListener此方法添加的事件处理程序只能使用removeEventListener来移除
并且,移除时,传入的参数必须和添加事件处理程序时的参数完全相同(否则将会移除不成功)
那么 什么叫参数完全相同呢,我们来看个例子

btn.addEventListener('click', function() {
	console.log('我是目标元素')
}, false)
btn.removeEventListener('click', function() {
	console.log('我是目标元素')
}false)  // 没有用

注意 上面例子中,表面上好像添加和删除时的参数是一样的,但是,大家不要忘了,对象和函数是复杂数据类型,任何两个复杂数据类型数据都是不完全相等的。因为它们比较的是指针,只有两个指向同一个内存空间的指针才是完全相等的。
所以,以上两个方法的第二个参数(也就是那个处理函数)其实是不相等的。因为它们是匿名函数

所以,总结出一点
当你添加一个事件处理程序时,是以匿名函数的形式添加的,那么这个事件处理程序将不可移除

那么如何才能正确移除呢,看下面的方法

var handleFunction = function () {
	console.log('我是目标元素')
}
btn.addEventListener('click', handleFunction, false)
btn.removeEventListener('click', handleFunction, false)  // 可正常移除

最后多一句嘴: 上面我们说过,事件捕获是存在兼容性问题的,故,在不必要的情况下,请尽量将事件处理程序添加到事件流的冒泡阶段。你懂的。。。

IE事件处理程序

IE的事件处理程序同样提供了两个方法

btn.attachEvent('onclick', function () {
    console.log('btn 被触发了')
})
btn.detachEvent('onclick', function () {
    console.log('btn 被触发了')
})

和addEvenListener以及removeEventListener不同的是:
1、IE的attachEvent 和 detachEvent只有两个参数,第一个是事件名,第二个是处理事件的函数。
2、由于 IE8 及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。也就是说通过此方法添加的事件处理函数,只有事件冒泡,没有事件捕获
3、通过attachEvent和detachEvent方法添加和移除事件时,事件名前面都必须加上单词on
4、IE11以下版本(不包括11,IE11及以上和edge都已不支持)浏览器均支持,其他浏览器都不支持,用时请注意
5、addEventListener 和 removeEventListener 以及DOM0级事件处理程序 btn.οnclick= function() {} 内部的this都是=是指向触发事件的元素的,而attachEvent 和 detachEvent 是始终在全局作用域下运行,故内部this 指向window

btn.attachEvent('onclick', function () {
    console.log(this === window)  // true
})
btn.detachEvent('onclick', function () {
    console.log(this === window)  // true
})
此时我们已经了解到了DOM0级的事件处理程序以及DOM2级的事件处理程序以及IE的事件处理,但是只有DOM0级事件处理程序是跨浏览器的,其他两个都存在兼容性问题。故,此时,我们是不是可以封装一个新的对象方法,从而来实现跨浏览器的事件处理呢,附上代码如下:
var EvenItem = {
	addHandler: function(element, type, handler) {
		if (element.addEventListener) {
			element.addEventListener(type, handler, false);
		} else if (element.attachEvent) {
			element.attachEvent("on" + type, handler);
		} else {
			element["on" + type] = handler;
		}
	},
	removeHandler: function(element, type, handler) {
		if (element.removeEventListener) {
			element.removeEventListener(type, handler, false);
		} else if (element.detachEvent) {
			element.detachEvent("on" + type, handler);
		} else {
			element["on" + type] = null;
		}
	}
}
var handler = function (event) {
	console.log(event.type)
}
// 使用方式如下
// 添加事件
EventItem.addHandleer(btn, 'click', handler)
// 移除事件
EventItem.removeHandler(btn, 'click', handler)

5、事件对象

什么是事件对象

简单理解为 包含所有与事件相关的信息的对象(包括事件的元素,事件类型,以及其他与事件有关的信息)
在DOM事件触发时,会产生这样一个事件对象event

IE中的事件对象和其他浏览器中的DOM事件对象有点不一样。下面我们分别来细说

1、非IE浏览器的事件对象

虽然对于不同的事件类型来说,事件对象中的属性和方法可能会有不一样的地方。但是不管哪种事件类型的事件对象,都会存在以下属性和方法
在这里插入图片描述

我们挑几个常用的来加以说明

a、preventDefault()

这个方法表示可以取消事件的默认行为,前提是必须cancelable属性为true时,调用才能生效
比如:当我们点击一个a标签时,会触发a标签跳转herf属性的url地址这个默认行为。当我们不需要产生跳转时,就可以通过preventDefault()去取消这个默认行为

b、stopPropagation()

这个方法表示阻止事件继续传播(可能是阻止事件冒泡,也可能是事件捕获),但前提是bubbles这个属性为true时,调用此方法才能生效

c、currentTarget 和 target

这两者有什么区别呢
currentTarget: 当前正在处理事件的那个元素
target: 事件的目标
咋一看,好像没太明白这两个的区别。没关心,我们看个例子就懂了

<body id="app">
	<div class="myDiv">
		myDiv
		<button class="btn">目标元素</button>
	</div>
</body>
<script>
	var btn = document.querySelector('.btn')
	var div = document.querySelector('.myDiv')
	var body = document.getElementById('app')

	// 我们做一个操作,那就是用鼠标点击 目标元素 按钮,由于事件冒泡的原因,故会同时触发btn,div,body3个元素的点击事件
	btn.onclick = function (event) {
		console.log('我是目标元素')
		console.log(event.currentTarget)   // btn
		console.log(event.target)    // btn
		console.log(event.currentTarget === this)   // true
		console.log(event.target === this)   // true
	}
	div.addEventListener('click', function(event) {
		console.log('我是div')
		console.log(event.currentTarget)   // div
		console.log(event.target)    // btn
		console.log(event.currentTarget === this)   // true
		console.log(event.target === this)   // false
	}, false)
	body.onclick = function (event) {
		console.log('我是body')
		console.log(event.currentTarget)   // body
		console.log(event.target)    // btn
		console.log(event.currentTarget === this)   // true
		console.log(event.target === this)   // false
	}
</script>

由以上代码以及输出结果我们就可以得出以下结论
event.target 表示的是真正被点击(或者其他操作)的元素 称为事件目标(我们鼠标点击的是 目标元素 那个按钮,故无论冒泡到哪个元素上执行处理程序,target都始终为被鼠标点击的那个 button)

event.currentTarget 表示的是当前正在处理事件的元素,故event.currentTarget始终等于this(比如 当前冒泡到body上触发了body的事件处理程序,那么对于这个事件处理程序而言,当前正在处理这个事件的元素就是body 故 event.currentTarget 就是body)

2、IE浏览器的事件对象

首先,对于IE而言,不同的事件处理程序 event事件对象将有不同的区别

a、DOM0级事件处理程序,event对象将会作为window对象的一个属性而存在
var btn = document.getElementById("myBtn");
btn.onclick = function(){
	var event = window.event;
	alert(event.type); //"click"
}; 

尽管如此,但IE9及以上版本的浏览器实际也已经实现了 将event对象作为处理函数的参数返回,具体看下面一段代码

btn.onclick = function (event) {
	console.log(event)       // undefind   IE9以下
	console.log(event)       // object  IE9及以上
	console.log(window.event)  // object 所有IE浏览器
}
b、attachEvent事件处理程序,event对象既作为处理函数的参数而存在,又作为window对象的一个属性而存在
var btn = document.getElementById("myBtn");
btn.attachEvent('onclick', function(event) {
	console.log(event)     // object  所有支持attachEvent的IE浏览器
	console.log(window.event)     // object  所有支持attachEvent的IE浏览器    两种方式均可获取event
}); 
IE浏览器的事件对象属性和方法也会因为事件类型的不同而有所区别,但同样,不管哪种事件类型的事件对象,都会存在以下属性或方法

在这里插入图片描述
其中,针对srcElement,我稍微做个说明:
srcElement 是和target属性相同的,而不是currentTarget,故srcElement表示的始终是真正被点击(或者其他操作)的目标元素

btn.attachEvent('onclick', function (event) {
     console.log('我是目标元素')
     console.log(event.srcElement)   // btn
     console.log(this)    // window
     console.log(event.srcElement === this)   // false
 })
 div.attachEvent('onclick', function (event) {
     console.log('我是div')
     console.log(event.srcElement)   // btn
     console.log(this)    // window
     console.log(event.srcElement === this)   // false
 })
 body.attachEvent('onclick', function (event) {
     console.log('我是body')
     console.log(event.srcElement)   // btn
     console.log(this)    // window
     console.log(event.srcElement === this)   // false
 })
3、跨浏览器的事件对象

虽然 DOM 和 IE 中的 event 对象不同,但基于它们之间的相似性我们是不是也可以拿出跨浏览器的方案来,附上代码

var EvenItem = {
	addHandler: function(element, type, handler) {
		if (element.addEventListener) {
			element.addEventListener(type, handler, false);
		} else if (element.attachEvent) {
			element.attachEvent("on" + type, handler);
		} else {
			element["on" + type] = handler;
		}
	},
	removeHandler: function(element, type, handler) {
		if (element.removeEventListener) {
			element.removeEventListener(type, handler, false);
		} else if (element.detachEvent) {
			element.detachEvent("on" + type, handler);
		} else {
			element["on" + type] = null;
		}
	},
	getEvent: function (event) {
		return event ? event : window.event
	},
	preventDefault : function (event) {
        if (event.preventDefault) {
            event.preventDefault()
        } else {
            event.returnValue = false
        }
    },
    stopPropagation: function (event) {
        if (event.stopPropagation) {
            event.stopPropagation()
        } else {
            event.cancelBubble = true
        }
    }
}
var handler = function (event) {
	console.log(event.type)
}
其实后面还有个事件类型,但是事件的类型太多了,我这里就不做过多说明了,大家感兴趣的可以自己找些资料去看看。关于事件,我们就说到这里了。说的不好或不对的地方,欢迎留言指出,万分感谢
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值