前几天掘金那篇什么挑战前端的文章很火,可是几十的赞几百的评论,说明这篇文章不是火在质量而是火在争议。客观的来说里面那道题仍是不错的,能帮助咱们理解js的一些机制。做者评论里面屡次提到前端版块文章质量的问题,我想说的是不止前端版块其余版块也同样,任何社区的都不可能保证全部的文章都是精品,只是掘金前端版块文章数量比较多,这个问题比较明显,可是每一个读者水平不同,你以为这篇讲的东西没什么用,但可能对于别人就颇有帮助。javascript
因此就如同评论下面不少人在说但愿掘金出一个拉黑屏蔽功能,能够拉黑做者,屏蔽文章。这个功能确实能够知足不少人的筛选文章的需求,可是催这个功能也不是一天两天了,官方可能有本身的计划一直没有提上开发日程。我以前其实没这个需求,顶多就是不太想看面试类的文章,可是那篇文章出来以后,我发现我仍是有拉黑需求的,起码那篇文章不该该出如今个人文章推荐列表中。虽然知道官方往后确定会出这个功能,可是远水解不了近渴,不如当下先本身开发一个插件来实现这个功能。php
特此说明,做者拉黑只是为了展现功能,并且为了效果特地选择了专栏比较多的高等级做者,对两位上镜的做者并没有冒犯之意。css
做者屏蔽html
标题关键字屏蔽前端
实现思路其实很是简单,首先要明确的一点是掘金的文章列表是后台请求回的数据,未来官方实现这个功能的时候能够后端直接返回过滤后的结果,也能够前端拿到数据时进行过滤处理再展现。而对于咱们外部插件来讲没有这个能力,只能经过将已经生成好的页面元素进行隐藏来达到咱们的目的。vue
咱们先来一个最简单的实现,首先咱们打开chrome的调试面板,看一下掘进首页文章列表的html结构:java
如图所示全部的文章以li
标签的形式,放在class
为entry-list
的ul
中,那么咱们如今要作的是选中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官网没写,可是一些国产浏览器插件市场也是有的,好比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...
})();
复制代码
文章列表的过滤隐藏和上面控制台输入代码版原理差很少,重点是触发隐藏操做的时机。因为数据异步请求,过早的话文章列表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,一个即便页面关闭也能够长期保留数据的方式,具体内容能够查看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的用法注意点,不知道新入行的朋友还会不会用到。
黑名单管理侧边栏是一开始就生成的,虽然当前页的操做会更新到侧边栏,可是一般在浏览过程当中咱们是打开多个页面的,那咱们要如何保持多个页面侧边栏的数据同步呢?显然跨浏览器标签页操做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随缘修复,感兴趣的能够参与共同维护,在官方出这功能以前,我们本身动手,丰衣足食。