Web前端最全前端进阶之路:点击事件绑定,前端初级面试题

计算机网络

  • HTTP 缓存

  • 你知道 302 状态码是什么嘛?你平时浏览网页的过程中遇到过哪些 302 的场景?

  • HTTP 常用的请求方式,区别和用途?

  • HTTPS 是什么?具体流程

  • 三次握手和四次挥手

  • 你对 TCP 滑动窗口有了解嘛?

  • WebSocket与Ajax的区别

  • 了解 WebSocket 嘛?

  • HTTP 如何实现长连接?在什么时候会超时?

  • TCP 如何保证有效传输及拥塞控制原理。

  • TCP 协议怎么保证可靠的,UDP 为什么不可靠?

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

算法

  • 链表

  • 字符串

  • 数组问题

  • 二叉树

  • 排序算法

  • 二分查找

  • 动态规划

  • BFS

  • DFS

  • 回溯算法

背景

我是一个前端小兵,我在一家互联网公司做做一些简单的业务开发。

某一天,我接到了一个需求,做一个抽奖功能。公司里的前辈们已经完成了业务逻辑,而且已经提供了业务功能的接口,只需要我制作页面并完成事件绑定即可。

实践

开动

我写好了页面,页面中有一个 ID 为 lucky-draw 的按钮元素。接下来,我需要为它绑定点击事件。我是这样写的:

var btn = document.getElementById(‘lucky-draw’)

btn.onclick = function () {

BX.luckyDraw()

}

这其中 BX.luckyDraw() 就是前辈们提供的业务接口,执行它就可以运行后续的抽奖功能。

我测试了一下,代码工作正常,于是很开心地准备上线。

第一关

然而前辈们告诉我,这些重要功能的按钮是需要加统计的。这也难不倒我,因为我很熟悉统计系统的 API。于是我修改了一下事件绑定的代码:

btn.onclick = function () {

BX.luckyDraw()

BX.track(‘lucky-draw’)

}

这样做是有效的,但前辈们又告诉我,因为某些原因,统计代码和业务代码是分布在不同位置的,以上代码需要拆开。于是我尝试这样修改:

btn.onclick = function () {

BX.luckyDraw()

}

// other codes…

btn.onclick = function () {

BX.track(‘lucky-draw’)

}

结果发现点击按钮时的抽奖功能失效了。原来,使用 .onclick 这样的事件属性来绑定事件有一个非常大的缺点,重复赋值会覆盖旧值。也就是说,这种方式只能绑定最后一次赋值的事件处理函数。

我硬着头皮去请教前辈,才知道原来这种方式早已经不推荐使用了,应该使用 DOM 标准的事件绑定 API 来处理(在旧版 IE 下有一些兼容性问题,这里不展开)。因此我的代码改成了这样:

btn.addEventListener(‘click’, function () {

BX.luckyDraw()

}, false)

// other codes…

btn.addEventListener(‘click’, function () {

BX.track(‘lucky-draw’)

}, false)

所有功能终于又正常了,我很开心地准备上线。

第二关

事实证明我还是太天真了,PM 是不会一次性把所有需求都告诉你的。原来,这个抽奖功能还需要做 A/B 测试,也就是说,只有一半的用户会看到这个抽奖功能。

这意味着用户的页面上可能根本没有 btn 这个元素,那么 btn.addEventListener(...) 这一句直接就抛错了。因此,在为按钮绑定事件处理函数之前,我不得不先判断一下:

if (btn) {

btn.addEventListener(‘click’, function () {

BX.luckyDraw()

}, false)

}

// other codes…

if (btn) {

btn.addEventListener(‘click’, function () {

BX.track(‘lucky-draw’)

}, false)

}

虽然这样的代码在所有用户的页面上都可以正常工作,但这些预先判断看起来很蛋疼啊。我再次带着疑惑向前辈请教。前辈慈祥地看着我,说出了一句经典名言:

傻瓜,为什么不用万能的 jQuery 呢?

原来,神奇的 jQuery 允许我们忽略很多细节,比如这种没有取到元素的情况会被它默默地消化掉。而且 jQuery 的事件绑定方法也不存在兼容性问题,API 也比较好看。不错不错,不管网上的大神们怎么喷 jQuery,但它简直是我的救星啊!

于是,我的代码变成了以下这样:

var $btn = $(‘#lucky-draw’)

$btn.on(‘click’, function () {

BX.luckyDraw()

})

// other codes…

$btn.on(‘click’, function () {

BX.track(‘lucky-draw’)

})

我的代码看起来像那么回事了,我很开心地准备上线。

第三关

当然,我的故事不会这么快结束。要知道,对一个有追求的前端团队来说,不断提升用户体验是永恒的目标。比如,我们网站使用了一些方法来提升页面加载性能,部分页面内容并不是原本存在于页面中的,而是在用户需要时,由 JavaScript 动态生成的。

拿这个抽奖功能来说,抽奖按钮存在于一个名为 “惊喜” 的 tab 中,而这个 tab 在初始状态下是没有内容的,只有当用户切换到这个 tab 时,才会由 JS 填充其内容。示意代码是这样的:

$(‘.tabs > .surprise’).on(‘click’, function () {

var htmlSurpriseTab = [

’,

‘Lucky Draw’,

‘’

].join(‘’)

$(‘.tab-panels > .surprise’).html(htmlSurpriseTab)

// BTN READY

})

这意味着,我写的事件绑定代码需要写在 // BTN READY 处。这种深层的耦合看起来很不理想,我需要想办法解决它。

我想起来,我在阅读 jQuery 文档时看到有一种叫作 “事件委托” 的方法,可以在元素还未添加到页面之前就为它绑定事件。于是,我尝试这样来写:

$(document.body).on(‘click’, ‘#lucky-draw’, function () {

BX.luckyDraw()

})

果然,我成功了!好事多磨啊,这个需求终于开心地上线了。

经过进一步的研究,我了解到 “事件委托” 的本质是利用了事件冒泡的特性。把事件处理函数绑定到容器元素上,当容器内的元素触发事件时,就会冒泡到容器上。此时可以判断事件的源头是谁,再执行对应的事件处理函数。由于事件处理函数是绑定在容器元素上的,即使容器为空也没有关系;只要容器的内容添加进来,整个功能就是准备就绪的。

虽然事件委托的原理听起来稍有些复杂,但由于 jQuery 对事件委托提供了完善的支持,我的代码并没有因此变得很复杂。

多想一步

经过这一番磨炼,我收获了很多经验值;同时,我也学会了更进一步去发现问题和思考问题。比如,在我们的网页,通常会有多个按钮,那为它们绑定事件的脚本代码可能就是这样的:

$body = $(document.body)

$body.on(‘click’, ‘#lucky-draw’, function () {

BX.luckyDraw()

})

$body.on(‘click’, ‘#some-btn’, function () {

// do something…

})

$body.on(‘click’, ‘#another-btn’, function () {

// do something else…

})

我隐隐觉得这样不对劲啊!虽然这些代码可以正常工作,但每多一个按钮就要为 body 元素多绑定一个事件处理函数;而且根据直觉,这样一段段长得差不多的代码是需要优化的。因此,如果我可以把这些类似的代码整合起来,那不论是在资源消耗方面,还是在代码组织方面,都是有益的。

于是,我尝试把所有这些事件委托的代码合并为一次绑定。首先,为了实现合并,我需要为这些按钮找到共同点。很自然地,我让它们具有相同的 class:

Lucky Draw

Button

Link

Link

然后,我试图通过一次事件委托来处理所有这些按钮:

$body.on(‘click’, ‘.action’, function () {

// WHEN CLICK ANY ‘.action’, WE COME HERE.

})

很显然,所有具有 action 类名的元素被点击后都会触发上面这个事件处理函数。那么,接下来,我们在这里区分一下事件源头,并执行对应的任务:

$body.on(‘click’, ‘.action’, function () {

switch (this.id) {

case ‘lucky-draw’:

BX.luckyDraw()

break

case ‘some-btn’:

// do something…

break

// …

}

})

这样一来,所有分散的事件委托代码就被合并为一处了。在这个统一的事件处理函数中,我们使用 ID 来区分各个按钮。

但 ID 有一些问题,由于同一页面上不能存在同名的元素,相信前端工程师们都对 ID 比较敏感,在日常开发中都尽量避免滥用。此外,如果多个按钮需要执行的任务相同,但它的 ID 又必须不同,则这些 ID 和它们对应的任务之间的对应关系就显得不够明确了。

于是,我改用 HTML5 的自定义属性来标记各个按钮:

Lucky Draw

Button

Link

打开全栈工匠技能包-1小时轻松掌握SSR

两小时精通jq+bs插件开发

生产环境下如歌部署Node.js

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

网易内部VUE自定义插件库NPM集成

谁说前端不用懂安全,XSS跨站脚本的危害

webpack的loader到底是什么样的?两小时带你写一个自己loader

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值