原文地址: JS面试题 跨浏览器的事件处理函数的绑定与解绑定
题目如题,还有一个条件,要保证事件处理函数的this正确指向绑定的元素。 说来惭愧,第一次做这道题的时候,根本不知道IE的attachEvent方法没有让事件处理函数的this正确指向所绑定的元素,就简单处理了一下跨浏览器的绑定与解绑定。 这样给IE的attachEvent方法传入通过bind方法处理过的函数就可以保证事件处理函数this的正确指向了。然而… 这样给IE的attachEvent传入的handler和用户传入LIZ.util.addEventListener的handler已经不是同一个函数,会造成IE下面无法将绑定的handler移除。如果既要保证this指向正确,还要保证能够跨浏览器绑定和解绑定,也就是既要用bind方法来改变IE中事件处理函数的context,又要能够通过handler引用的比较来解绑定事件处理函数,那么必须在传入的handler 和 bind方法返回的函数之间建立映射关系。但是用handler引用做key来写map的话是不行的,比如 因为key值只能是string类型吧,handler大概是按照String(handler)被自动转换了,这样做key值的就是handler的内容而非其引用了。 map不能用来做映射,但还有array。可以用2个数组来记录映射关系。 只要把这2个数组记在element上就行了,做法是丑陋了点,但这样是行的通的……其实这个方法是写此文章想引出下面的做法的时候才想到的,根据逻辑关系走就想到这样做了。但其实一开始就想到是因为这些事件处理函数是基于浏览器自身的api来处理的,所以才有不兼容的现象,如果将这些事件都纳入自己代码的管理,那要兼容事件处理就要比上面的方法优雅的多了。那么,Observer 观察者模式登场…… 关于观察者模式,个人认为《head first 设计模式》是讲的非常形象,容易理解的。如果有这样一个观察者类,我们就能够将事件处理函数的绑定和解绑定纳入自己的管理,轻松的实现跨浏览器的绑定和解绑定。只要新建一个Observer的实例,将所有向目标element注册的事件处理函数都注册到该观察者实例上,然后让该观察者向目标element注册一批事件处理函数来观察目标element,这样在目标element上发生的任何事件都可以通过该观察者向事件处理函数传递了。这样也就可以规避之前handler与proxyHandler的映射问题以及this指向的问题。 在dom节点上存取数据不知道有什么要注意的地方,有空了还要读读jQuery的源码。 本博客文章由LiZn创作或分享,以创作公用CC 姓名标示-非商业性-相同方式分享 3.0 Unported 授权条款共享。
1
var
LIZ = {
2
util : {
3
addEventListener :
function
( element, type, handler ){
4
if
( element.addEventListener ){
5
element.addEventListener(type, handler,
false
);
6
}
else
if
( element.attachEvent ){
7
element.attachEvent(
'on'
+type, handler);
8
}
else
{
9
element[
'on'
+type] = handler;
10
}
11
},
12
removeEventListener :
function
( element, type, handler ){
13
if
( element.removeEventListener ){
14
element.removeEventListener(type, handler,
false
);
15
}
else
if
( element.dettachEvent ){
16
element.dettachEvent(
'on'
+type, handler);
17
}
else
{
18
element[
'on'
+type] =
null
;
19
}
20
}
21
}
22
};
那次面试就这样悲剧了-。-回来之后开始研究怎么做。以上代码倒是可以跨浏览器绑定和解绑事件处理函数,但是this指向在IE下是不正确的。让this指向正确很简单,使用handler.call(element)或者handler.apply(element)就可以了么……突然发现这个handler不是由我来调用的,傻X了…… 记得一般会有类似于jQuery.proxy函数的bind方法,可以返回指定context调用的方法,简单一点就是
1
LIZ.patterns = {
2
bind :
function
( fn, context ){
3
return
function
(){
4
return
fn.apply( context, arguments );
5
};
6
}
7
};
1
var
LIZ = {
2
util : {
3
addEventListener :
function
( element, type, handler ){
4
if
( element.addEventListener ){
5
element.addEventListener(type, handler,
false
);
6
}
else
if
( element.attachEvent ){
7
element.attachEvent(
'on'
+type, LIZ.patterns.bind( handler, element ) );
8
}
else
{
9
element[
'on'
+type] = handler;
10
}
11
},
12
removeEventListener :
function
( element, type, handler ){
13
if
( element.removeEventListener ){
14
element.removeEventListener(type, handler,
false
);
15
}
else
if
( element.dettachEvent ){
16
element.dettachEvent(
'on'
+type, handler);
17
}
else
{
18
element[
'on'
+type] =
null
;
19
}
20
}
21
}
22
};
1
var
handler =
function
( arg ){
return
arg; };
2
var
anotherHandler =
function
( arg ){
return
arg; };
3
var
map = {};
4
map[handler] = 1;
5
alert( map[anotherHandler] );
//会alert 1
1
var
key = [], val = [], proxyHandler = LIZ.patterns.bind( handler, element );
2
3
//attachEvent时向数组中添加记录
4
key.push( handler );
5
val.push( proxyHandler );
6
7
//dettachEvent时通过循环比较key中的值来找到
8
//proxyHandler 然后解绑定 再删除key val中的记录
9
for
(
var
i = 0; i < key.length ; i++ ){
10
if
( key[i] === handler ){
11
element.detachEvent( type, val[i] );
12
key.splice( i, 1 );
13
val.splice( i, 1 );
14
break
;
15
}
16
}
1
LIZ.patterns.Observer =
function
(target){
2
this
.target = target ||
this
;
3
this
.listeners = {};
4
};
5
6
LIZ.patterns.Observer.prototype = {
7
constructor : LIZ.patterns.Observer,
8
addListener :
function
(type, handler){
9
if
(
typeof
this
.listeners[type] ==
'undefined'
){
10
this
.listeners[type] = [];
11
}
12
this
.listeners[type].push(handler);
13
},
14
fire :
function
(event){
15
var
handlers =
this
.listeners[event.type],
16
i = 0;
17
if
( handlers
instanceof
Array ){
18
for
( ; i < handlers.length ; i++ ){
19
handlers[i].call(
this
.target, event);
20
}
21
}
22
},
23
removeListener :
function
(type, handler){
24
var
handlers =
this
.listeners[type],
25
i = 0;
26
if
( handlers
instanceof
Array ){
27
for
( ; i < handlers.length ; i++ ){
28
if
( handlers[i] === handler ){
29
break
;
30
}
31
}
32
handlers.splice(i,1);
33
}
34
}
35
};
1
var
LIZ = {
2
dom : {
3
getData :
function
(element, name) {
4
if
( element ){
5
if
(
typeof
element.liz ===
'object'
){
6
return
element.liz[name];
7
}
8
}
9
return
undefined;
10
},
11
setData :
function
(element, name, data) {
12
if
( element ){
13
if
(
typeof
element.liz !==
'object'
){
14
element.liz = {};
15
}
16
element.liz[name] = data;
17
}
18
},
19
removeData :
function
(element, name) {
20
this
.setData(element, name,
null
);
21
},
22
trigger :
function
( element, event ) {
23
if
(
this
.getData(element,
'observer'
)
instanceof
LIZ.patterns.Observer ){
24
this
.getData(element,
'observer'
).fire(event);
25
}
26
},
27
addEventListener :
function
(element, type, handler){
28
var
observer =
this
.getData(element,
'observer'
),
29
proxyHandler =
function
(event){
30
observer.fire(event);
31
};
32
if
( !observer || !(observer
instanceof
LIZ.patterns.Observer) ){
33
observer =
new
LIZ.patterns.Observer(element);
34
this
.setData(element,
'observer'
, observer);
35
}
36
if
(
typeof
observer[type] ==
'undefined'
){
37
if
( element.addEventListener ){
38
element.addEventListener(type, proxyHandler,
false
);
39
}
else
if
( element.attachEvent ){
40
element.attachEvent(
'on'
+type, proxyHandler);
41
}
else
{
42
element[
'on'
+type] = proxyHandler;
43
}
44
}
45
observer.addListener(type, handler);
46
},
47
removeEventListener :
function
(element, type, handler){
48
var
observer =
this
.getData(element,
'observer'
);
49
50
if
( observer
instanceof
LIZ.patterns.Observer ){
51
observer.removeListener(type, handler);
52
}
else
{
53
if
( element.removeEventListener ){
54
element.removeEventListener(type, handler,
false
);
55
}
else
if
( element.detachEvent ){
56
element.detachEvent(
'on'
+type, handler);
57
}
else
{
58
element[
'on'
+type] =
null
;
59
}
60
}
61
}
62
};
希望本文能够对你有所帮助,欢迎留言讨论,如果你喜欢本站文章,可以使用该 RSS订阅地址来订阅本站。
相关推荐
以下是一些关于React组件、事件处理、高阶组件(HOC)、Render Props以及Hooks的经典面试题及其答案: 一、React组件基础 1. React事件机制: React使用事件委托策略,它不会将事件直接绑定到DOM元素上,而是监听...
对于中高级React开发者,了解和掌握一些核心概念和面试题是非常重要的。以下是一些关于React.js的面试知识点,涵盖组件基础、事件处理、高阶组件以及新特性。 1. **组件基础** - **React事件机制**:React不直接将...
以下是一些关于React.js组件基础、事件处理、高阶组件、Render Props以及Hooks的知识点: 1. **React组件基础**: - React组件是构成应用的基本单元,可以复用和独立管理状态。 - 组件可以通过JSX(JavaScript与...
面试中,React的知识点通常涵盖组件化、状态管理、生命周期方法、事件处理、性能优化等。以下是一些基于提供的内容提炼的关键知识点: 1. **React事件机制**: - React事件处理不同于原生JavaScript。它使用事件...
面试中,React组件、事件处理、高阶组件(HOC)、Render Props和Hooks是常见的考察点。下面详细讲解这些知识点。 1. **React组件基础** - React组件是构建应用的基本单元,可以看作是独立的、可重用的代码模块。...
以下是一些关于React面试题及其答案的详细解释: 一、组件基础 1. React事件机制 React不直接在DOM元素上绑定事件,而是采用事件代理的方式,将所有事件监听器添加到document上。当事件冒泡到document时,React的...
根据给定的C++面试题,我们可以深入探讨和解析其中涉及的关键知识点,这些知识点涵盖了C++语言的基础到高级特性,包括动态内存管理、指针、类继承与多态、构造函数与析构函数、虚函数以及运算符重载等。 ### 动态...
- **指针与引用**:清楚指针和引用的区别,掌握指针的动态绑定和解引用操作。 2. **C++模板** - **函数模板**:理解模板的泛型编程思想,如何创建和使用函数模板。 - **类模板**:学习如何定义和实例化类模板,...
以下是一些关于C++的常见面试题及其详细解释: 1. **C中的static作用**: - 隐藏作用:static关键字用于局部变量时,使其在函数内部保持其状态,即使函数结束,变量的值也会保留,下次调用时仍可访问。 - 持久化...
Vue.js 是一款流行的前端JavaScript框架,它以组件化、易学习和高性能著称。在面试过程中,面试官通常会从核心概念、技术要点、实战能力等多个角度来考察候选人的Vue技能。以下是一些针对Vue面试的关键知识点的详细...
以下是一些基于C和C++面试题的知识点详解: 1. **内存管理**: - **栈与堆的区别**:栈是自动分配和回收的,用于存储函数参数、局部变量等;堆则是程序员手动通过`malloc`或`new`分配,`free`或`delete`释放。 - ...
自定义指令通常涉及`bind`、`inserted`、`update`、`componentUpdated`和`unbind`钩子函数,分别在绑定、元素插入、值更新、组件更新和解绑时执行。 掌握这些面试知识点,可以帮助你在Vue.js面试中表现出色,更好地...
《阿里巴巴校园招聘历年经典面试题汇总:C++研发 1》 阿里巴巴作为全球知名的互联网公司,其校园招聘面试题往往涵盖了广泛的技术领域,对于应聘C++研发岗位的候选人来说,这些题目是准备面试的重要参考资料。以下是...
在如何利用SharpZipLib进行文件的压缩和解压缩.txt 怎样打开关闭CDROM.txt 怎样检测网络中的电脑是否有安装SQL2000.txt 怎样写一个存储过程类来实现如何对存储过程的调用?.txt 直接拉入图片文件到程序窗口中...
【C/C++ 80页面试题】是一份涵盖了软件公司面试常见问题的综合资料,尤其适合初入行业的求职者学习。这份资料包含了函数求解、引用的概念及其使用等多个知识点。 首先,我们来看一个函数求解的例子: ```cpp int ...