对简书官网个人中心页面右侧内容块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标签是这样的<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代码的人尽量不要用那些用来渲染样式的选择器来查找元素,不要怕麻烦,自己重新定义选择器。

全文完。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 142,965评论 18 613
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 10,568评论 1 90
  • 前端开发面试知识点大纲: HTML&CSS: 对Web标准的理解、浏览器内核差异、兼容性、hack、CSS基本功:...
    秀才JaneBook阅读 1,077评论 0 23
  • 第5章 菜单、按钮及导航 一、下拉菜单 小伙伴们注意,在Bootstrap框架中的下拉菜单组件是一个独立的组件,根...
    凛0_0阅读 3,148评论 0 67
  • 我是一位普通的不能再普通的家庭农妇。 去年,刚上初中的儿子放暑假后,就和他几个小学同学混在了一起。 那几个孩子四、...
    小头头阅读 52评论 2 3