浅谈JavaScript之事件绑定


Posted in Javascript onJuly 08, 2013

其实没有什么新的知识点,只是为了方便其他有需要的朋友们翻阅,对自己而言也算是一个积累,所以只能算是闲谈 JavaScript,老鸟们可以尽情飘过。
在进入正题之前,先提个问题热热身吧。
现在有如下 HTML 结构:

<div id="wrap">
 <input type="button" value="按钮一" />
 <input type="button" value="按钮二" />
 <input type="button" value="按钮三" />
 <input type="button" value="按钮四" />
 <input type="button" value="按钮五" />
</div>

以及如下 JavaScript 代码:
var wrap = document.getElementById('wrap'), 
    inputs = wrap.getElementsByTagName('input'); for (var i = 0, l = inputs.length; i < l; i++) { 
    inputs[i].onclick = function () { 
        alert(i); 
    } 
}

请问,这样执行的结果是什么?
/***************************分割线***************************/
如果你的回答是“点击按钮时, alert 当前按钮的索引值 i”,那你就中了我的圈套了。大家不妨试试,无论你点击哪个按钮,它都会alert(5)。

这个看似理所当然的结果为什么会和实际情况不同呢?其实也是很好理解的。
因为 onclick 只是事件绑定,而不是执行,当我们执行 onclick 事件的时候,这时的 i 已经是循环以后的值了,照这样看,每个按钮都alert(5) 也就不足为奇了。

那么,如果我们要怎么实现“点击按钮时,alert 当前按钮的索引值 i”呢?这里就要用到 JavaScript 中暗藏玄机的一个概念“闭包”。我们可以用闭包的方式改写以上 JS,把 for 循环中的 i 值保存在内存中,代码如下:

var wrap = document.getElementById('wrap'), 
    inputs = wrap.getElementsByTagName('input'); for (var i = 0, l = inputs.length; i < l; i++) { 
    (function (cur) { 
        inputs[cur].onclick = function () { 
            alert(cur); 
        } 
    })(i) 
}

再试试效果?确实能 alert 出相应的索引值了,不过至此为止还只是开胃菜,正题才刚刚开始!
以上的方法,我们是通过循环 + 闭包给 button 按钮上绑定事件,我们知道,在 JavaScript 中函数也是对象,对象就会占用内存,现在的例子中只有 5 个按钮,或许你会认为这样的性能开销可以忽略不计,但是如果当我们有 50个,甚至 500 个按钮的时候,IE 已经哭了,当有更多其他性能隐患并发时,所有的浏览器都哭了。

回到刚才的例子,我们可以用“事件委托”的方法来解决这个因绑定事件随着按钮增加而可能导致的性能问题。原理很简单,利用 Javascript 的事件冒泡,我们可以把事件的绑定从按钮移到它们的父级元素上,不管按钮有多少,它们只有一个共同的父级元素,那样我们只需要绑定一次事件就可以了。
代码如下:

var wrap = document.getElementById('wrap'), 
    inputs = wrap.getElementsByTagName('input'); wrap.onclick = function (ev) { 
    var ev = ev || window.event, 
    target = ev.target || ev.srcElement; 
    for (var i = 0, l = inputs.length; i < l; i++) { 
        if (inputs[i] === target) { 
            alert(i) 
        } 
    } 
}

至此,正餐完毕,我们还可以再深入一下,来些餐后甜点。
除了在性能上,事件委托比闭包的事件绑定更有优势以外,事件委托还无需顾及子元素(即被绑定事件的元素)的数量。比如,我们在 onclick 事件绑定以后,增加一个按钮:
var newInput = document.createElement('input'); 
newInput.setAttribute('type', 'button'); 
newInput.setAttribute('value', '按钮六'); 
wrap.appendChild(newInput);

同样在最后加了这段代码的闭包方式和事件委托方式,我们可以看到,闭包实现的事件绑定中点击“按钮六”毫无效果,但是在事件委托中实现的事件绑定点击“按钮六”则会有 alert。相反,如果我们要删除一个按钮,闭包的方式仍会在内存中保存已删除按钮的 onclick 事件(除非手动设为 null),事件委托则不会对内存造成多余的负担,就为这个原因,我们也应该多加利用事件委托的方式来绑定同一层级的多个元素。
Javascript 相关文章推荐
JavaScript窗口功能指南之在窗口中书写内容
Jul 21 Javascript
JavaScript.The.Good.Parts阅读笔记(二)作用域&amp;闭包&amp;减缓全局空间污染
Nov 16 Javascript
使用JavaScript检测Firefox浏览器是否启用了Firebug的代码
Dec 28 Javascript
ajax请求get与post的区别总结
Nov 04 Javascript
js无刷新操作table的行和列
Mar 27 Javascript
vue2 前后端分离项目ajax跨域session问题解决方法
Apr 27 Javascript
Angular2使用Angular-CLI快速搭建工程(二)
May 21 Javascript
Vue cli+mui 区域滚动的实例代码
Jan 25 Javascript
JS/jQuery实现简单的开关灯效果【案例】
Feb 19 jQuery
详细教你微信公众号正文页SVG交互开发技巧
Jul 25 Javascript
小程序点击图片实现png转jpg
Oct 22 Javascript
node.js 使用 net 模块模拟 websocket 握手进行数据传递操作示例
Feb 11 Javascript
JS Map 和 List 的简单实现代码
Jul 08 #Javascript
利用JS实现浏览器的title闪烁
Jul 08 #Javascript
利用js实现遮罩以及弹出可移动登录窗口
Jul 08 #Javascript
使用jquery实现简单的ajax
Jul 08 #Javascript
从数据结构的角度分析 for each in 比 for in 快的多
Jul 07 #Javascript
JavaScript 上万关键字瞬间匹配实现代码
Jul 07 #Javascript
20行代码实现的一个CSS覆盖率测试脚本
Jul 07 #Javascript
You might like
PHP连接数据库实现注册页面的增删改查操作
2016/03/27 PHP
用XMLDOM和ADODB.Stream实现base64编码解码实现代码
2010/11/28 Javascript
JavaScript之appendChild、insertBefore和insertAfter使用说明
2010/12/30 Javascript
几种延迟加载JS代码的方法加快网页的访问速度
2013/10/12 Javascript
JavaScript中的typeof操作符用法实例
2014/04/05 Javascript
Jquery跳到页面指定位置的方法
2014/05/12 Javascript
JavaScript setTimeout使用闭包功能实现定时打印数值
2015/12/18 Javascript
关于JavaScript 原型链的一点个人理解
2016/07/31 Javascript
手机Web APP如何实现分享多平台功能
2016/08/19 Javascript
轻松实现js选项卡切换效果
2016/09/24 Javascript
原生js实现回复评论功能
2017/01/18 Javascript
jquery 判断是否支持Placeholder属性的方法
2017/02/07 Javascript
解决vue-cli中stylus无法使用的问题方法
2017/06/19 Javascript
各种选择框jQuery的选中方法(实例讲解)
2017/06/27 jQuery
微信小程序实现上传图片功能
2018/05/28 Javascript
微信小程序左滑删除实现代码实例
2019/09/16 Javascript
VUE实现强制渲染,强制更新
2019/10/29 Javascript
JavaScript 中的执行上下文和执行栈实例讲解
2021/02/25 Javascript
[01:45:05]VGJ.T vs Newbee Supermajor 败者组 BO3 第二场 6.6
2018/06/07 DOTA
[46:14]完美世界DOTA2联赛PWL S3 Magma vs INK ICE 第一场 12.11
2020/12/16 DOTA
深入理解NumPy简明教程---数组1
2016/12/17 Python
python3库numpy数组属性的查看方法
2018/04/17 Python
Scrapy框架使用的基本知识
2018/10/21 Python
python实现词法分析器
2019/01/31 Python
python3 selenium自动化测试 强大的CSS定位方法
2019/08/23 Python
Python发起请求提示UnicodeEncodeError错误代码解决方法
2020/04/21 Python
在Pytorch中使用Mask R-CNN进行实例分割操作
2020/06/24 Python
pycharm 实现调试窗口恢复
2021/02/05 Python
法国家具及室内配件店:home24
2017/01/21 全球购物
Spongelle官网:美国的创意护肤洗护品牌
2019/05/15 全球购物
学校群众路线专项整治方案
2014/10/31 职场文书
党支部考察鉴定意见
2015/06/02 职场文书
新手初学Java List 接口
2021/07/07 Java/Android
Spring Boot 排除某个类加载注入IOC的操作
2021/08/02 Java/Android
python周期任务调度工具Schedule使用详解
2021/11/23 Python
详解Go语言中配置文件使用与日志配置
2022/06/01 Golang