【实践思考】本身开发一个掘金黑名单功能插件

前几天掘金那篇什么挑战前端的文章很火,可是几十的赞几百的评论,说明这篇文章不是火在质量而是火在争议。客观的来说里面那道题仍是不错的,能帮助咱们理解js的一些机制。做者评论里面屡次提到前端版块文章质量的问题,我想说的是不止前端版块其余版块也同样,任何社区的都不可能保证全部的文章都是精品,只是掘金前端版块文章数量比较多,这个问题比较明显,可是每一个读者水平不同,你以为这篇讲的东西没什么用,但可能对于别人就颇有帮助。javascript

因此就如同评论下面不少人在说但愿掘金出一个拉黑屏蔽功能,能够拉黑做者,屏蔽文章。这个功能确实能够知足不少人的筛选文章的需求,可是催这个功能也不是一天两天了,官方可能有本身的计划一直没有提上开发日程。我以前其实没这个需求,顶多就是不太想看面试类的文章,可是那篇文章出来以后,我发现我仍是有拉黑需求的,起码那篇文章不该该出如今个人文章推荐列表中。虽然知道官方往后确定会出这个功能,可是远水解不了近渴,不如当下先本身开发一个插件来实现这个功能。php

需求说明

  1. 将不喜欢的做者加入黑名单,在首页和搜索文章时再也不显示黑名单中做者的文章。
  2. 将不喜欢的关键字加入标题黑名单,在首页和搜索文章时再也不显示标题中包含关键字的文章。
  3. 在文章详情页面能够随时拉黑做者。
  4. 能够对黑名单列表进行管理(添加和删除)。

成品展现

特此说明,做者拉黑只是为了展现功能,并且为了效果特地选择了专栏比较多的高等级做者,对两位上镜的做者并没有冒犯之意。css

做者屏蔽html

标题关键字屏蔽前端

方案思路

实现思路其实很是简单,首先要明确的一点是掘金的文章列表是后台请求回的数据,未来官方实现这个功能的时候能够后端直接返回过滤后的结果,也能够前端拿到数据时进行过滤处理再展现。而对于咱们外部插件来讲没有这个能力,只能经过将已经生成好的页面元素进行隐藏来达到咱们的目的。vue

控制台输入代码版

咱们先来一个最简单的实现,首先咱们打开chrome的调试面板,看一下掘进首页文章列表的html结构:java

如图所示全部的文章以li标签的形式,放在classentry-listul中,那么咱们如今要作的是选中entry-list下的全部li标签并查找里面的内容,若是存在css关键字就隐藏掉这个li标签。node

//$$是大部分浏览器调试控制台都支持的API,等价于document.querySelectorAll,平时开发不能用。
$$('.entry-list>li').forEach(item => {
    //innerText是一个节点及其后代的“渲染”文本内容,判断里面是否包含css,不区分大小写。
    if (/css/i.test(item.innerText)) {
        //符合要求的元素隐藏
        item.hidden = true;
    }
});
复制代码

在控制台输入这段代码,你就会发现文章列表里面的包含css关键字的条目(这个不光是标题)都被隐藏掉了。jquery

可是滚动条下滑后加载的文章仍是会显示的,若是想隐藏只能在控制台再运行一遍这个代码。git

油猴脚本版

咱们要作的是一个完整的插件,而不能像上面那样人工手动在控制台输入代码,虽然你写一个完整的插件代码控制台跑也是能够,功能效果实际上是同样的,但终究不是长远之计。

首先咱们能够把这个功能作成一个浏览器插件,好比根据Chrome的规范和API创建项目开发,最后打包成crx格式的文件,Chrome浏览器就能够本地安装这个插件,或者花点钱成为谷歌开发者发布到谷歌应用商店。

浏览器插件的好处是,能够调用更多浏览器级的API,使用浏览器为原生插件提供的相关功能,可是对于咱们只针对一个网站提供的小功能来讲,有些杀鸡用牛刀了,并且这个插件也只有Chrome能用,若是其余浏览器想用还要再开发,好比FireFox就使用的是xpi格式的扩展。

一种更好的方式就是使用Tampermonkey

Tampermonkey(国内习惯称其为油猴)是一个浏览器插件,是最为流行的用户脚本管理器,并且在 Chrome, Microsoft Edge, Safari, Opera Next, 和 Firefox都有对应的版本,也就是说咱们写的脚本能够经过它跑在各个浏览器上。而咱们今天就是经过油猴脚本的形式来实现咱们的功能。

对浏览器插件开发更感兴趣的朋友推荐这篇文章:【干货】Chrome插件(扩展)开发全攻略

方案实现

安装Tampermonkey

Tampermonkey的安装说简单也简单,说不简单也不简单,简单的是Tampermonkey被收录在各个浏览器的官方插件市场,很轻易的就能够搜索下载,并且不只限于前面提到的那些浏览器,一些移动端浏览器也是支持的,还有虽然Tampermonkey官网没写,可是一些国产浏览器插件市场也是有的,好比360极速浏览器,搜狗浏览器。不简单的缘由嘛,就是有些浏览器的插件市场我们很差访问。

若是浏览器插件市场确实没有,只要是chromium内核的,一般都支持crx本地安装。

安装完成后咱们能够在浏览器上点击油猴插件图标,能够在管理面板中查看,开启,删除,管理咱们的脚本,能够在获取新脚本中找到几个脚本市场的网站,直接下载别人写好的脚本。

建立脚本文件

咱们要本身开发脚本的话,选择添加新的脚本就能够打开一个脚本编写页面,可是建议仍是在本身编辑器里写,有提示和格式化更方便。

建立后的脚本默认有一些代码以下,就是头部像是注释同样的东西,这里要用到的简单说明一下,更多内容能够参考官方文档

// ==UserScript==
// @name 掘金文章黑名单 <-插件名,会显示在管理面板里
// @namespace https://github.com/hoc2019/blackList <-脚本命名空间
// @version 0.1 <-版本号
// @description 掘金文章黑名单过滤脚本 <-描述
// @author wangzy <-做者
// @match https://juejin.im/* <-只有匹配到的网站才会使用该脚本
// @require https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js <-引入脚本,这里用了jQuery
// @grant GM_addStyle <-给网站注入css样式的方法
// ==/UserScript==

(function() {
 'use strict';

    // Your code here...
})();
复制代码

文章列表过滤 - MutationObserver观察dom节点变化

文章列表的过滤隐藏和上面控制台输入代码版原理差很少,重点是触发隐藏操做的时机。因为数据异步请求,过早的话文章列表dom节点尚未被建立,过滤操做就会失败,而过晚的话就会肉眼可见列表的消失。

一个简单粗暴的方式就是写一个轮询,不断的去检测文章列表dom节点是否存在,存在则表示文章加载完成,能够进行过滤操做了。同理滚动到底部加载文章,也可经过轮询检测文章列表dom节点数量是否改变来触发过滤操做(固然也能够检测滚轮事件)。可是轮询自己就是一个在性能和体验上找平衡的方法,轮询间隔短了判断就准确,体验就好,可是会消耗不少性能,反之则性能好,体验就差。

因此这里咱们使用MutationObserver,MutationObserver能够用来监听DOM节点的变化,是旧的Mutation Events功能的替代品,详细内容能够参考MDN的文档

大概的使用方式以下:

const container = $('#juejin');
const config = {
    attributes: false,      // 检测节点属性变化,这里用不到,为减小没必要要的触发这里不用开启
    childList: true,        // 检测子节点添加和删除
    subtree: true           // 检测包含后代节点
};
const mutationCallback = mutationsList => {
    for (let mutation of mutationsList) {
        const type = mutation.type;
        const addedNodes = mutation.addedNodes;  //增长节点数组
        // 会根据上面的的设置触发相应事件,这里能够判断触发的事件类型
        switch (type) {
            case 'childList':
                //由于咱们是要判断列表加载,只用处理节点增长时的事件便可
                if (addedNodes.length > 0) {
                    list = $('.entry-list');
                    if (list.children().length) {
                        //中止观察
                        loadObserver.disconnect();
                        //过滤操做
                        filter(list.children());
                        //建立滚动后加载观察
                        createNodeListener(list[0], config, updateLoad);
                    }
                }
                break;
        }
    }
};
//建立首次加载观察
const loadObserver = createNodeListener(container[0], config, handleLoad);
//定义一个建立观察者的工厂函数,方便建立观察者。
function createNodeListener(node, config, mutationCallback) {
    const observer = new MutationObserver(mutationCallback);
    observer.observe(node, config);
    return observer;
}
复制代码

因为是每次文章列表节点发生变化就去处理,因此响应很及时,不太会出现条目先显示再消失的状况,并且是被动触发的,并不会像轮询同样,后台不断在获取查询dom节点信息。

这里可能会有疑问为何中间要从新建立一个新的观察,由于一开始的观察是基于一个很是顶层的容器(id为juejin的div节点,一开始只有它存在),文章列表以外的一些其余DOM节点变化也会触发事件,因此当咱们拿到文章列表的容器后(class为entry-list的ul),就中止对顶层容器的观察改成只观察文章列表容器,这样咱们就能够精确的只响应文章列表节点的变更。

经过MutationObserver构造函数建立完观察者后不会当即启动观察,须要经过观察者者调用observe方法,须要注意的是observe方法的第一个参数必须是DOM Node (多是一个Element) ,经过jQuery获取的节点要转化为原生dom节点,例如$('#juejin')[0]这样。

黑名单数据的保存 - localStorage数据存取

黑名单数据存储在localStorage,一个即便页面关闭也能够长期保留数据的方式,具体内容能够查看MDN的文档

使用方式很简单,但要注意的是localStorage存储的只能是字符串,若是想保存数组和对象须要作一下序列化处理,转成json字符串。

//存储
const authorBlackList = ['小四', '小五'];
localStorage.setItem('authorBlackList', JSON.stringify(authorBlackList));

//读取
const authorBlackList = JSON.parse(localStorage.getItem('authorBlackList'));
复制代码

存储后的数据能够打开浏览器调试的Application面板查看,因为是本地保存浏览器清空localStorage就会致使数据清除,跨浏览器也是不能共用数据的,服务端存储的话须要成本,这事仍是交给官方来干吧。

黑名单管理侧边栏

这个也比较好实现,写好html,经过jQuery建立节点插入页面就能够了,css样式的话要经过油猴脚本提供的方法GM_addStyle插入,前面有提到过。

大概就是下面这个样子:

//css样式
const myScriptStyle ='.black-sidebar{background:#000}'
GM_addStyle(myScriptStyle);

//html结构
const sidebarHtml = '<div class="black-sidebar"><ul><ul></div>';
$('body').append($(sidebarHtml));
复制代码

可能惟一注意的点就是侧边栏中黑名单列表是后期能够动态添加的,因此点击事件不能直接经过jquery的click方法绑定在生成的li标签上,而是要经过on方法绑定在父级ul上,就和事件委托一个道理。不过这些都是jQuery的用法注意点,不知道新入行的朋友还会不会用到。

不一样页面侧边栏数据同步 - visibilitychange事件监听

黑名单管理侧边栏是一开始就生成的,虽然当前页的操做会更新到侧边栏,可是一般在浏览过程当中咱们是打开多个页面的,那咱们要如何保持多个页面侧边栏的数据同步呢?显然跨浏览器标签页操做dom是不现实的,可是咱们能够去监听一个标签页的切换,当该标签页为显示状态时,从新获取一下数据并更新列表。

咱们经过visibilitychange事件来监听,该事件详情能够参考MDN文档

document.addEventListener('visibilitychange', function() {
    if (document.visibilityState === 'visible') {
        // 更新侧边栏数据
        updateSidebarList();
        if (pathname === 'post') {
            //若是是详情页,更新拉黑按钮状态
            updateBtnState();
        }
    }
});

复制代码

效果以下:

上面document.visibilityState是只读属性表示标签页状态,visible表示显示,还有hidden表示隐藏。因此经过该方法还能够检测到标签页的隐藏,移动端浏览器切换到后台s,媒体声音还在继续播放的问题,能够经过这个事件手动暂停解决。

文章详情页拉黑按钮

这个也很简单,不过在作的时候碰到一个有意思的地方,原本想着建立一个和上面分享按钮同样的元素,添加一样的类名就能够了,可是掘金的样式里类选择器里还带了data-v-xxx数据(vue防止css样式污染使用的scoped特性),因而干脆直接克隆了一个分享按钮的节点,再对内容做了修改。

最后

这个插件基本功能已经完成,可是因为开发的时候目的就是为了自用,没有考虑兼容性,也没有各类状况下的测试,只是本身跑通能用就行.

其实仍是不少点能够优化,好比如今添加删除黑名单后要页面刷新才能生效,一些dom操做还能够减小,代码能够进一步抽象封装,面向对象等等。因此这篇文章的目的并非推广这个插件,而是简单的分享一下脚本的开发流程,和里面解决某些问题的思路,能引发某些人关于脚本开发的兴趣就足够了,这也是这个系列文章的目的。

这个脚本的代码我会放到Github,有需求的能够用用试试,脚本也已经放到GreasyFork脚本市场上了,能够直接搜索掘金文章黑名单下载使用,可是后续尚未什么更新计划,bug随缘修复,感兴趣的能够参与共同维护,在官方出这功能以前,我们本身动手,丰衣足食。

脚本仓库地址 文章备份仓库地址

相关文章
相关标签/搜索