`
LiZn
  • 浏览: 10272 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

JS面试题 跨浏览器的事件处理函数的绑定和解绑定

阅读更多

 

原文地址: JS面试题 跨浏览器的事件处理函数的绑定与解绑定

题目如题,还有一个条件,要保证事件处理函数的this正确指向绑定的元素。

说来惭愧,第一次做这道题的时候,根本不知道IE的attachEvent方法没有让事件处理函数的this正确指向所绑定的元素,就简单处理了一下跨浏览器的绑定与解绑定。

 

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 };

这样给IE的attachEvent方法传入通过bind方法处理过的函数就可以保证事件处理函数this的正确指向了。然而…

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 };

这样给IE的attachEvent传入的handler和用户传入LIZ.util.addEventListener的handler已经不是同一个函数,会造成IE下面无法将绑定的handler移除。如果既要保证this指向正确,还要保证能够跨浏览器绑定和解绑定,也就是既要用bind方法来改变IE中事件处理函数的context,又要能够通过handler引用的比较来解绑定事件处理函数,那么必须在传入的handler 和 bind方法返回的函数之间建立映射关系。但是用handler引用做key来写map的话是不行的,比如

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

因为key值只能是string类型吧,handler大概是按照String(handler)被自动转换了,这样做key值的就是handler的内容而非其引用了。

map不能用来做映射,但还有array。可以用2个数组来记录映射关系。

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 }

只要把这2个数组记在element上就行了,做法是丑陋了点,但这样是行的通的……其实这个方法是写此文章想引出下面的做法的时候才想到的,根据逻辑关系走就想到这样做了。但其实一开始就想到是因为这些事件处理函数是基于浏览器自身的api来处理的,所以才有不兼容的现象,如果将这些事件都纳入自己代码的管理,那要兼容事件处理就要比上面的方法优雅的多了。那么,Observer 观察者模式登场……

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         iftypeof 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 };

关于观察者模式,个人认为《head first 设计模式》是讲的非常形象,容易理解的。如果有这样一个观察者类,我们就能够将事件处理函数的绑定和解绑定纳入自己的管理,轻松的实现跨浏览器的绑定和解绑定。只要新建一个Observer的实例,将所有向目标element注册的事件处理函数都注册到该观察者实例上,然后让该观察者向目标element注册一批事件处理函数来观察目标element,这样在目标element上发生的任何事件都可以通过该观察者向事件处理函数传递了。这样也就可以规避之前handler与proxyHandler的映射问题以及this指向的问题。

1 var LIZ = {
2     dom : {
3         getData : function (element, name) {
4             if ( element ){
5                 iftypeof element.liz === 'object' ){
6                     return element.liz[name];
7                     }
8                 }
9             return undefined;
10         },
11         setData : function (element, name, data) {
12             if ( element ){
13                 iftypeof 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             iftypeof 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 };

 

在dom节点上存取数据不知道有什么要注意的地方,有空了还要读读jQuery的源码。

本博客文章由LiZn创作或分享,以创作公用CC 姓名标示-非商业性-相同方式分享 3.0 Unported 授权条款共享。 
希望本文能够对你有所帮助,欢迎留言讨论,如果你喜欢本站文章,可以使用该 RSS订阅地址来订阅本站。

 

1
3
分享到:
评论

相关推荐

    搜集了众多经典高频React.js 面试题完整高清版(带全部答案).pdf

    以下是一些关于React组件、事件处理、高阶组件(HOC)、Render Props以及Hooks的经典面试题及其答案: 一、React组件基础 1. React事件机制: React使用事件委托策略,它不会将事件直接绑定到DOM元素上,而是监听...

    有哪些必知必会的React.js 面试题值得收藏(适合中高级).pdf

    对于中高级React开发者,了解和掌握一些核心概念和面试题是非常重要的。以下是一些关于React.js的面试知识点,涵盖组件基础、事件处理、高阶组件以及新特性。 1. **组件基础** - **React事件机制**:React不直接将...

    最全的IT公司React.js 面试题你一定要看!(本人用的).pdf

    以下是一些关于React.js组件基础、事件处理、高阶组件、Render Props以及Hooks的知识点: 1. **React组件基础**: - React组件是构成应用的基本单元,可以复用和独立管理状态。 - 组件可以通过JSX(JavaScript与...

    2022最新react面试题精选(最全+详细答案).pdf

    面试中,React的知识点通常涵盖组件化、状态管理、生命周期方法、事件处理、性能优化等。以下是一些基于提供的内容提炼的关键知识点: 1. **React事件机制**: - React事件处理不同于原生JavaScript。它使用事件...

    2021必备react面试题完整高清版(已面试成功).pdf

    面试中,React组件、事件处理、高阶组件(HOC)、Render Props和Hooks是常见的考察点。下面详细讲解这些知识点。 1. **React组件基础** - React组件是构建应用的基本单元,可以看作是独立的、可重用的代码模块。...

    助你面试顺利!最新react面试题附答案(已面试成功).pdf

    以下是一些关于React面试题及其答案的详细解释: 一、组件基础 1. React事件机制 React不直接在DOM元素上绑定事件,而是采用事件代理的方式,将所有事件监听器添加到document上。当事件冒泡到document时,React的...

    C++面试题.

    根据给定的C++面试题,我们可以深入探讨和解析其中涉及的关键知识点,这些知识点涵盖了C++语言的基础到高级特性,包括动态内存管理、指针、类继承与多态、构造函数与析构函数、虚函数以及运算符重载等。 ### 动态...

    中兴c++面试题集合

    - **指针与引用**:清楚指针和引用的区别,掌握指针的动态绑定和解引用操作。 2. **C++模板** - **函数模板**:理解模板的泛型编程思想,如何创建和使用函数模板。 - **类模板**:学习如何定义和实例化类模板,...

    16个常用的c++面试题.docx

    以下是一些关于C++的常见面试题及其详细解释: 1. **C中的static作用**: - 隐藏作用:static关键字用于局部变量时,使其在函数内部保持其状态,即使函数结束,变量的值也会保留,下次调用时仍可访问。 - 持久化...

    vue面试题,面试实例

    Vue.js 是一款流行的前端JavaScript框架,它以组件化、易学习和高性能著称。在面试过程中,面试官通常会从核心概念、技术要点、实战能力等多个角度来考察候选人的Vue技能。以下是一些针对Vue面试的关键知识点的详细...

    关于C和C++的面试题集

    以下是一些基于C和C++面试题的知识点详解: 1. **内存管理**: - **栈与堆的区别**:栈是自动分配和回收的,用于存储函数参数、局部变量等;堆则是程序员手动通过`malloc`或`new`分配,`free`或`delete`释放。 - ...

    vue面试题有哪些是经常问的?

    自定义指令通常涉及`bind`、`inserted`、`update`、`componentUpdated`和`unbind`钩子函数,分别在绑定、元素插入、值更新、组件更新和解绑时执行。 掌握这些面试知识点,可以帮助你在Vue.js面试中表现出色,更好地...

    阿里巴巴校园招聘历年经典面试题汇总:C++研发 1

    《阿里巴巴校园招聘历年经典面试题汇总:C++研发 1》 阿里巴巴作为全球知名的互联网公司,其校园招聘面试题往往涵盖了广泛的技术领域,对于应聘C++研发岗位的候选人来说,这些题目是准备面试的重要参考资料。以下是...

    ASP升级.net资料大全(c#入门 语言规范 源码教程 学习笔记 技术资料 面试题 asp与.net代码生成器)

    在如何利用SharpZipLib进行文件的压缩和解压缩.txt 怎样打开关闭CDROM.txt 怎样检测网络中的电脑是否有安装SQL2000.txt 怎样写一个存储过程类来实现如何对存储过程的调用?.txt 直接拉入图片文件到程序窗口中...

    C\C++ 80页面试题

    【C/C++ 80页面试题】是一份涵盖了软件公司面试常见问题的综合资料,尤其适合初入行业的求职者学习。这份资料包含了函数求解、引用的概念及其使用等多个知识点。 首先,我们来看一个函数求解的例子: ```cpp int ...

Global site tag (gtag.js) - Google Analytics