DOM2级事件兼容处理
THIS问题
bindDOM2():
在IE6~8中使用attachEvent绑定事件的时候,因为不支持bind方法,所以不能用bind方法解决。
var tempFn = function () {
eventFn.call(curEle);
};
通过在外层包括一个化妆函数,调用这个化妆函数时调用需要绑定的函数.call(curEle),并为这个化妆函数增加一个photo属性用于保存化妆之前的函数,便于删除的时候对比函数。
tempFn.photo = eventFn;
为当前元素创建一个自定义属性“myBind”+ evenType,它保存当前元素对应事件类型绑定的化妆函数,以便后期清除。
if (!curEle["myBind" + evenType]) {
curEle["myBind" + evenType] = [];
}
curEle["myBind" + evenType].push(tempFn);
curEle.attachEvent("on" + eventType, tempFn);
unbindDOM2():
通过当前元素的“myBind”属性找到绑定的化妆函数,对比化妆函数.photo确定是不是这个要移除这个化妆后的函数,找到后把事件和化妆函数移除掉。
var ary = curEle["myBind" + evenType];
for (var i = 0; i < ary.length; i++) {
if (ary[i].photo === eventFn) {
ary.splice(i, 1);
curEle.detachEvent("on" + evenType, ary[i]);
break;
}
}
重复问题
bindDOM2():
上面实现的bindDOM2(),可以想同一个元素中同一个行为添加相同的事件函数,存在重复问题。
每一次自己在往自定义属性对应的容器中添加前,看一下之前是否已经有了,有的话就不用再重新的添加了,同理也不需要再往事件池中存储了
var ary = curEle["myBind" + eventType];
for (var i = 0; i < ary.length; i++) {
var cur = ary[i];
if (cur.photo === eventFn) {
return;
}
}
unbindDOM2():
顺序问题
bindDOM2():
不用浏览器自带的事件池,而是自己模拟标准浏览器的事件池
自己创建一个事件池,把需要触发的函数按顺序放到事件池中。为当前元素绑定一个run方法,当run方法执行的时候去事件池中获取同类型的方法数组。然后依次执行这些方法,执行的时候使用call(HTMLElement,e)使THIS指向触发元素,使方法的一个参数变为处理后的window.event。
// run: 我们只给当前元素的带年纪行为绑定一个方法run,当点击事件执行的时候执行的是run方法,我在run方法中根据自己存储的方法顺序分别的再把这些方法执行
function run(e) {
e = e || window.event;
var flag = e.target ? true : false; // IE6~8下不兼容e.target,得到的flag为false
if (!flag) {
e.target = e.srcElement;
e.pageX =
e.clientX +
(document.documentElement.scrollLeft || doucment.body.scrollLeft);
e.pageY =
e.clientY +
(document.documentElement.scrollTop || doucment.body.scrollTop);
e.preventDefault = function () {
e.returnValue = false;
};
e.stopPropagation = function () {
e.cancelBubble = true;
};
}
// this:当前点击的这个元素curEle e.target存储的是当前触发的元素curEle
// 获取自己事件池中绑定的那些方法,并且让这些方法依次的执行
var ary = e.target["myEvent" + e.type];
for (var i = 0; i < ary.length; i++) {
var tempFn = ary[i];
tempFn.call(this, e);
}
}
unbindDOM2():
function off(curEle, eventType, eventFn) {
var ary = curEle["myEvent" + eventType];
for (var i = 0; i < ary.length; i++) {
var cur = ary[i];
if (cur === eventFn) {
ary.splice(i, 1);
}
}
}
整个代码
<!--
* @Author: lemon
* @Date: 2020-09-17 15:15:13
* @LastEditTime: 2020-09-17 17:58:04
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: React前端准备事件DOM2事件.html
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#box {
margin: 20px auto;
200px;
height: 200px;
background: lightblue;
}
</style>
</head>
<body>
<div id="box"></div>
<script type="text/javascript">
var box = document.getElementById("box");
function con() {
console.log(this);
}
box.addEventListener("click", con, false);
box.removeEventListener("click", con, false);
// 处理DOM2级事件绑定的兼容性问题(绑定方法)
function bindDOM2(curEle, evenType, eventFn) {
if ("addEventListener" in document) {
curEle.addEventListener(evenType, eventFn, false);
return;
}
// eventFn化妆,并且把化妆前的照片贴在自己对应的脑门上
var tempFn = function () {
eventFn.call(curEle);
};
tempFn.photo = eventFn;
if (!curEle["myBind" + evenType]) {
curEle["myBind" + evenType] = [];
}
// 解决重复问题:每一次自己在往自定义属性对应的容器中添加前,看一下之前是否已经有了,有的话就不用再重新的添加了,同理也不需要再往事件池中存储了
var ary = curEle["myBind" + eventType];
for (var i = 0; i < ary.length; i++) {
var cur = ary[i];
if (cur.photo === eventFn) {
return;
}
}
curEle["myBind" + evenType].push(tempFn);
curEle.attachEvent("on" + eventType, tempFn);
}
function unbindDom2(curEle, eventType, eventFn) {
if ("removeEventListener" in document) {
curEle.removeEventListener(eventType, eventFn, false);
return;
}
var ary = curEle["myBind" + evenType];
for (var i = 0; i < ary.length; i++) {
if (ary[i].photo === eventFn) {
ary.splice(i, 1);
curEle.detachEvent("on" + evenType, ary[i]);
break;
}
}
}
// on:创建事件池,并且把需要给当前元素绑定的方法依次的增加到事件池中
function on(curEle, evenType, eventFn) {
if (!curEle["myEvent" + evenType]) {
curEle["myEvent" + evenType] = [];
}
var ary = curEle["myEvent" + evenType];
for (var i = 0; i < ary.length; ++i) {
var cur = ary[i];
if (cur === eventFn) {
return;
}
}
ary.push(eventFn);
// 执行on的时候, 我们给当前元素绑定了一个点击的行为,当点击得时候执行run方法:
// run方法中的this是当前元素curEle, 并且浏览器给run传递了一个MouseEvent对象
bindDOM2(curEle, eventType, run);
}
// off:在自己的事件池中把某一个方法移除
function off(curEle, eventType, eventFn) {
var ary = curEle["myEvent" + eventType];
for (var i = 0; i < ary.length; i++) {
var cur = ary[i];
if (cur === eventFn) {
ary.splice(i, 1);
}
}
}
// run: 我们只给当前元素的带年纪行为绑定一个方法run,当点击事件执行的时候执行的是run方法,我在run方法中根据自己存储的方法顺序分别的再把这些方法执行
function run(e) {
e = e || window.event;
var flag = e.target ? true : false; // IE6~8下不兼容e.target,得到的flag为false
if (!flag) {
e.target = e.srcElement;
e.pageX =
e.clientX +
(document.documentElement.scrollLeft || doucment.body.scrollLeft);
e.pageY =
e.clientY +
(document.documentElement.scrollTop || doucment.body.scrollTop);
e.preventDefault = function () {
e.returnValue = false;
};
e.stopPropagation = function () {
e.cancelBubble = true;
};
}
// this:当前点击的这个元素curEle e.target存储的是当前触发的元素curEle
// 获取自己事件池中绑定的那些方法,并且让这些方法依次的执行
var ary = e.target["myEvent" + e.type];
for (var i = 0; i < ary.length; i++) {
var tempFn = ary[i];
tempFn.call(this, e);
}
}
</script>
</body>
</html>