首先声明这是一篇技术分析文章,要输出的信息就是如何分析定位web页面缺陷,并无其它恶意哦@简叔。
事情是这样的,某天我女朋友在电脑上浏览自己简书文章阅读量时发现,最新文章
,最新动态
,热门文章
三个tab选项来回切换时,tab项的选中状态有问题。我一听立马就激动起来了(原谅我,职业病犯了),心想简书这么优秀的平台怎么可能会有这种bug呢?打开简书的web页面看了下,果真存在这个问题,平时都没留意过,不得说女朋友还真是个心细的菇凉(微笑脸)。
背景介绍完了,下面就进入分析过程了。
bug复现
这里就拿@简叔大人的主页来做分析了。进入@简叔的主页你会发现右侧内容首页展示的是最新文章的列表,顶部的tab选项默认选中了最新文章
项。为啥?因为最新文章
下面有明显的黑色下划线啊,就是下图红色框中的样子。
然后你再点一下最新动态
的选项,你看到了什么,当然是@简叔在简书里面的最新动态了。but,不知道你有没有发现顶部的tab选项为啥有两项都是选中状态,也就是下图红色框中的样子(如果bug还没修复的话)。
这里的现象就是这篇文章要讨论的主角了。正常交互应该是用户查看最新动态
时,只有最新动态
这一项是选中状态,其它项都是非选中状态,因为下面的内容列表一次只能显示一种类型的内容。
好了,既然bug已经复现,下面我们就来分析下bug出现原因以及解决办法。
bug分析
碰到这种页面问题,我们程序猿要做的一件事情是什么?当然是打开浏览器的开发者工具了,没有意外的话应该是按下快捷键F12,这里使用的google浏览器,当然你也可以使用任何webkit内核的浏览器来调试。
点击开发者工具中控制台上的选择元素按钮,然后把鼠标放到最新文章
tab选项上,点击之后你会在控制台的Elements项下发现对应的html代码,应该就是下图中的样子。我们可以发现,这个tab切换使用了很常见的ul
li
结构实现。每一个tab项都是一个li
元素,对于选中的tab项,会在对应的li
元素上加一个active
类,所以我们可以发现包裹最新文章
和最新动态
的li
元素上都有active
类名。为啥会这样啊?因为这是一个bug啊。为啥这么明显的bug没发现呢?因为程序猿着急上线,加班加点忙到半夜,忘了还有一个bug没改,直接就上线了,上完线直接回去睡觉了,产品经理没有回归体验啊,好吧,这些都是我瞎猜的。其实这个bug的诱因就是用户在点击最新动态
tab项,给这个tab项增加active
类时,没有清除其它tab项上的active
类。具体的原因下面的解决方法中会有分析。
心细的读者会发现包裹tab项的a
标签上有data-pjax
属性,熟悉H5的同学应该对pjax技术并不陌生,就是一种利用H5中的pushState
方法以及ajax
技术无刷新更新页面数据的技术,具体看以参看jquery的pjax插件,github地址点这里。
bug解决方法
bug都分析完了,下面就是来看看能不能找到解决方法了。我们在bug分析的时候知道,在用户点击tab项时会给li
元素增加一个active
类,这个类肯定是在tab项的点击事件触发时候添加到li
标签上的,所以在这个页面对应的业务js代码中肯定会有active
这个字符串,知道这个下面就容易了,我们只要找到业务js代码,然后在业务js代码里面搜索active
字符串,就可以定位到tab项点击事件处理代码位置了。
打开开发者工具控制台选择Sources项,我们会发现简书网站从很多域加载文件,这么多域我如何才能快速找到业务代码呢?其实现在稍具规模的网站的都会使用cdn来存放自己站点需要的静态资源来加速站点访问,cdn服务一般来说都会利用二级域名做映射,当然不排除也有例外。知道这个点,我们就可以知道要在那个域中找业务js代码了。简书站点相关静态文件存放在cdn.qn0.jianshu.io
域下,打开这个域下的每一个js文件,然后搜索"active"
字符串,找到含有这个字符串的js文件,然后利用开发者工具自带的代码美化工具把压缩的js代码重新格式化排版,然后你就会发现下图中的代码片段。
在业务js代码中我们会发现下面这段代码:
$("#list-container").on("pjax:success", function(t) {
return function(n) {
var r;
return $("ul.nav-relationships li").removeClass("active"),
r = $(n.relatedTarget),
r.parent().addClass("active"),
$(window).scroll(),
e.initSharedAt(),
t.infiniteScroll()
}
}(this))
每次pjax
执行成功之后都会执行这段事件处理代码,在代码中我们也可以发现有$("ul.nav-relationships li").removeClass("active"),
这种操作,但是为啥没生效呢?别急,我们来看下这里的removeClass
是对那个标签元素操作的。我们把这里的选择元素的操作$("ul.nav-relationships li")
放到开发者工具控制台执行下,就会发现这里的选择器并没有找到元素,对空元素进行removeClass
当然不生效了。
回到图3的步骤,我们发现包裹tab项的ul
标签中并没有nav-relationships
这个类,ul
标签是这样的<ul class="nav nav-tabs nav-articles">
,也就是说通过nav-relationships
类是找不到包裹tab项的ul
标签的,我们可以使用nav-articles
类来寻找包裹tab项的ul
标签。
事件处理代码改成下面的样子:
$("#list-container").on("pjax:success", function(t) {
return function(n) {
var r;
return $("ul.nav-articles li").removeClass("active"),
r = $(n.relatedTarget),
r.parent().addClass("active"),
$(window).scroll(),
e.initSharedAt(),
t.infiniteScroll()
}
}(this))
然后就可以解决这个问题,上面的代码已经通过代码注入的方式验证可以解决这个bug,这里就不详细说了。
导致这个问题最有可能的原因就是写页面样式和写页面业务js代码的不是同一个人,写页面业务js代码的人使用静态页面样式中的一个类来查找元素,第一版的时候不会有问题,但是一旦后续版本页面样式有更新,而写页面业务js代码的人不知道那些地方更新了,直接拿来集成了,就会导致这种查找不到元素的问题,这也告诫写业务js代码的人尽量不要用那些用来渲染样式的选择器来查找元素,不要怕麻烦,自己重新定义选择器。
全文完。