程序员

对简书官网个人中心页面右侧内容块tab切换bug的分析

首先声明这是一篇技术分析文章,要输出的信息就是如何分析定位web页面缺陷,并无其它恶意哦@简叔。

事情是这样的,某天我女朋友在电脑上浏览自己简书文章阅读量时发现,最新文章,最新动态,热门文章三个tab选项来回切换时,tab项的选中状态有问题。我一听立马就激动起来了(原谅我,职业病犯了),心想简书这么优秀的平台怎么可能会有这种bug呢?打开简书的web页面看了下,果真存在这个问题,平时都没留意过,不得说女朋友还真是个心细的菇凉(微笑脸)。

背景介绍完了,下面就进入分析过程了。

bug复现

这里就拿@简叔大人的主页来做分析了。进入@简叔的主页你会发现右侧内容首页展示的是最新文章的列表,顶部的tab选项默认选中了最新文章项。为啥?因为最新文章下面有明显的黑色下划线啊,就是下图红色框中的样子。

图1:首次进入默认选中最新文章tab项

然后你再点一下最新动态的选项,你看到了什么,当然是@简叔在简书里面的最新动态了。but,不知道你有没有发现顶部的tab选项为啥有两项都是选中状态,也就是下图红色框中的样子(如果bug还没修复的话)。

图2: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类。具体的原因下面的解决方法中会有分析。

图3:bug分析

心细的读者会发现包裹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代码重新格式化排版,然后你就会发现下图中的代码片段。

图4:bug代码定位

在业务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标签是这样的