原文地址: 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订阅地址来订阅本站。
相关推荐
JavaScript面试题,JS面试题,WEB前端面试题下载.pdf
c++面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试题面试...
JavaScript面试题汇总,内含答案JavaScript面试题汇总,内含答案JavaScript面试题汇总,内含答案JavaScript面试题汇总,内含答案JavaScript面试题汇总,内含答案JavaScript面试题汇总,内含答案JavaScript面试题汇总...
javascript面试题汇总javascript面试题汇总javascript面试题汇总
前端面试相关-浏览器事件循环
关于Ajax的常见面试题 1,Ajax和javascript的区别? javascript是一种在浏览器端执行的脚本语言,Ajax是一种创建交互式网页应用的开发技术 ,它是利用了一系列相关的技术其中就包括javascript。 Javascript是由...
js面试题,里面有jquery和ajax等方面的面试题
react面试题 react_react面试题之事件绑定方式
不错的一本javascript面试题,你看了会喜欢的
js面试
最新web面试题css浏览器的兼容性问题.docx
JavaScript资源
2022java面试题、JVM面试题、多线程面试题、并发编程、Redis面试题、MySQL面试题、Java2022面试题、Netty面试题、Elasticsearch面试题、Tomcat面试题、Dubbo面试题、Kafka面试题、Linux面试题、2021面试题、java面试...
c语言面试题----main函数
JAVA面试题JAVA面试题JAVA面试题JAVA面试题JAVA面试题JAVA面试题
2023java最新阿里巴巴面试题2023java最新阿里巴巴面试题2023java最新阿里巴巴面试题2023java最新阿里巴巴面试题2023java最新阿里巴巴面试题2023java最新阿里巴巴面试题2023java最新阿里巴巴面试题2023java最新阿里...
75个JavaScript面试题集锦,内含解答,自测 JS 掌握程度.pdf
Vue.js面试题整理带答案pdf_vue.js面试题及答案.pdf
html+css+js面试题,比较全面系统的选择题复习资料!好好复习!
前端面试笔记总结,html ,css ,html5 ,css3 ,js,vue基础的面试题,HTML&CSS: 对Web标准的理解、浏览器内核差异、兼容性、hack、CSS基本功:布局、盒子模型、选择器优先级及使用、HTML5、CSS3、移动端适应。