上一篇文章 <https://www.cnblogs.com/oppoic/p/10186265.html>
 通过“content-scripts”的方式向页面注入js和css来美化页面,但是有一个弊端:一旦配置好需要注入的页面,之后如果这个页面地址以后发生变化,或者要新加一些URL进来,那么得修改manifest.json这个文件。试想如果一个Chrome插件已经打包好,再去改代码是不可能的。


本文通过另一种方式来实现相同的功能,同时做到页面地址动态可配置。下图是本次要处理的页面:https://jiacrontab.iwannay.cn/download/



每一行都是一个文件的基本信息,包括文件名、日期和文件大小,文件名格式统一为:项目名-版本-操作系统-平台(分类要大于等于三个,用 “-”
分隔,这是使用本插件的前提)。现在文件还少,查看不是很困难,如果以后文件多了,那么找一个文件是比较困难的。

这里又不太可能在服务端读取download目录下的文件,然后分类、分页展示,那只能在客户端浏览器上想办法了,Chrome插件可以实现这个功能。

一、小试牛刀

开发Chrome插件第一步,新建一个manifest.json文件,并按官方文档的要求配置一些必要参数
{ "name": "WebFileFilterPro", "version": "1.0.0", "description": "fast sort
your webpage files", "icons": { "16": "images/16.png", "48": "images/48.png",
"128": "images/128.png" }, "browser_action": { "default_icon": "images/16.png",
"default_title": "WebFileFilterPro" }, "manifest_version": 2 }
name:插件名称

version:版本号

description:插件描述

icons:图标,不同尺寸用于不同地方

browser_action:右上角插件栏的图标信息,包括:图标的图片路径、鼠标划上去提示的文字

manifest_version:固定为2

注:更多manifest.json的配置参考:官方文档 <https://developer.chrome.com/extensions/manifest>
  看看现在的项目结构



一个简单的没有任何功能的Chrome插件就完成了,去Chrome浏览器里安装下试试:打开Chrome浏览器 - 更多工具 - 扩展程序,打开“开发者模式”
- 加载已解压的扩展程序 - 选择src目录 - 确定



由图可见,manifest.json里配置的插件名称、版本号、插件描述等信息都体现了

二、加大难度

本插件的目的就是向特定页面注入一些js和css文件,达到美化页面的效果。现在来新建一个配置页面



 

把需要美化的页面地址通过配置页面保存到Local
Storage浏览器缓存里面,这样做的好处是:需要美化的地址随时可变,同时发现需要美化的页面直接加进去就可以了,不用修改代码。

这个时候大杀器“background”就要出场了,官方描述是这样的

Extensions are event based programs used to modify or enhance the Chrome
browsing experience. Events are browser triggers, such as navigating to a new
page, removing a bookmark, or closing a tab. Extensions monitor these events in
their background script, then react with specified instructions.


大致意思是,“background”是常驻Chrome浏览器后台运行的,可以捕获到很多行为:页面跳转、移除书签、关闭一个tab页等等。这里不需要那么强大的功能,只需要获取用户打开的页面地址,是不是在配置页面配置的地址即可。

现在改下“manifest.json”文件,加入新的配置
{ "name": "WebFileFilterPro", "version": "1.0.0", "description": "fast sort
your webpage files", "icons": { "16": "images/16.png", "48": "images/48.png",
"128": "images/128.png" }, "browser_action": { "default_icon": "images/16.png",
"default_title": "WebFileFilterPro" }, "options_page": "settings.html",
"permissions": [ "tabs", "http://*/*", "https://*/*" ], "background": {
"scripts": [ "js/background.js" ], "persistent": false }, "manifest_version": 2
}
options_page:配置页面地址(右键右上角插件图标,可以通过“选项”进入配置页面)

background:后台配置,包括js文件名和持久性设置

permissions:申请权限列表

1)tabs:获取用户访问页面得URL地址,必须得有tabs权限

2)http/https:向目标页面里注入css和js文件需要的权限

注:完整权限api请访问:Declare Permissions
<https://developer.chrome.com/extensions/declare_permissions>

现在来看下官方说的可以常驻后台运行的“background.js”到底可以获取到哪些东西
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { if
(changeInfo.status == "complete") { var url = tab.url; console.log("用户访问:" +
url); console.log("缓存里设置的页面:" + localStorage.url); }; });
在页面首次打开或者刷新事件(onUpdated)里获取“用户访问的页面地址”和“配置页面里面配置的地址”,看看控制台输出



 

两个地址都正确的获取到了,如果用户访问的地址和配置的地址吻合,那么向这个页面注入css和js文件
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { if
(changeInfo.status == "complete") { var url = tab.url; console.log(url); if
(localStorage.url != undefined && localStorage.url != '') { var urlList =
localStorage.url.split("\n"); if (urlList.indexOf(url) != -1) {
chrome.tabs.insertCSS(tabId, { file:"css/bootstrap.min.css" });
chrome.tabs.executeScript(tabId, { file:"js/jquery.min.js" });
chrome.tabs.executeScript(tabId, { file:"js/bootstrap.min.js" });
chrome.tabs.executeScript(tabId, { file:"js/filelist.js" }); } } }; });

使用“chrome.tabs.insertCSS”和“chrome.tabs.executeScript”这两个api完成css和js的注入。有了jQuery和Bootstrap的注入,页面就可以随意美化了。“filelist.js"代码如下
//console.log("filelist.js"); var fileList = []; var preList =
document.getElementsByTagName("pre"); if (preList.length == 1) { var lineList =
preList[0].innerHTML.split("\n"); //console.log(lineList); if (lineList.length
> 0) { var splitLinetext = []; var splitFileName = []; var fileName = '';
$.each(lineList,function (i, v) { //console.log(v); if (i == 0) { return true;//
continue; } if (v == undefined || v == "") { var line = parseInt(i); line += 1
; console.log("line:" + line + " is empty"); return true; } splitLinetext =
v.split(/\s+/); if (splitLinetext.length != 5) { console.log(splitLinetext);
return true; } fileName = splitLinetext[1].match(/>(\S*)</)[1]; //
console.log(fileName); if (fileName == null || fileName == '') { console.log(
"fileName is or empty"); return true; } splitFileName = fileName.split('-'); if
(splitFileName.length < 3) { console.log(splitFileName); return true; }
fileList.push({ PartA: splitFileName[0], PartB: splitFileName[1], FileTime:
formatDate(splitLinetext[2] + ' ' + splitLinetext[3]), FileSize: splitLinetext[4
], FileName: fileName }); });//console.log(fileList); var nodeDoctype =
document.implementation.createDocumentType('html', '', ''); if
(document.doctype) { document.replaceChild(nodeDoctype, document.doctype); }else
{ document.insertBefore(nodeDoctype, document.childNodes[0]); } $(
"html").attr("lang", "en"); $("head").html('<head><meta charset="UTF-8"><link
rel="shortcut icon" href="" /><meta name="viewport"
content="width=device-width, initial-scale=1.0"><meta
http-equiv="X-UA-Compatible" content="ie=edge"><title>FileList</title></head>'
); $("[rel='shortcut icon']").attr("href",
chrome.extension.getURL("images/16.png")); $(
"body").removeAttr("bgcolor").html('<nav class="navbar navbar-default
navbar-static-top"><div class="container"><div class="row"><div class="col-sm-2
col-md-2 col-lg-2"></div><div class="col-sm-10 col-md-10 col-lg-10"><div
class="navbar-header"><button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#navbar" aria-expanded="false"
aria-controls="navbar"><span class="sr-only">Toggle navigation</span><span
class="icon-bar"></span><span class="icon-bar"></span><span
class="icon-bar"></span></button></div><div id="navbar" class="collapse
navbar-collapse"><ul class="nav
navbar-nav"></ul></div></div></div></div></nav><div class="container"><div
class="row"><div id="nav" class="col-sm-2 col-md-2 col-lg-2"><ul class="nav
nav-pills nav-stacked"></ul></div><div class="col-sm-10 col-md-10
col-lg-10"><table id="table" class="table table-bordered"></table><ul
class="pager"><span id="pageIndexSpan"></span> / <span
id="pageSizeSpan"></span> / <span
id="totalCountSpan"></span>  <li id="previousPageLi"><a
href="javascript:;">Prev</a></li> <li id="nextPageLi"><a
href="javascript:;">Next</a></li></ul></div></div></div>'); var partAs =
getDistinctPartA(); $.each(partAs,function (idx, val) { $("#navbar
ul").append("<li data-value=\"" + val + "\"><a href='javascript:;'><strong>" +
val + "</strong></a></li>"); }); if (partAs.length > 0) { if
(localStorage.partA != undefined && $.inArray(localStorage.partA, partAs) != -1
) navBarLiClick(localStorage.partA);else navBarLiClick(partAs[0]); } else { $(
"body").html('<div class="container"><div class="row"><div class="col-sm-12
col-md-12 col-lg-12"><h3>no data, pls wait and refresh this page
:)</h3></div></div></div>'); } } else { console.log("can't find any line in
<pre> tag"); } } else { console.log("page's <pre> tag count illegal:" +
preList.length); } $("body").on("click", "#navbar ul li", function () {
navBarLiClick($(this).attr("data-value")); }); $("body").on("click", "#nav ul
li",function () { navLiClick($(this).attr("data-parta"), $(this
).attr("data-partb")); }); $("body").on("click", "#table tbody tr", function ()
{ $(this).css("background", "#DCDCDC").siblings().css("background", ""); }); $(
"body").on("click", "#previousPageLi",function () { if (localStorage.pageIndex
!= 1) { localStorage.pageIndex = parseInt(localStorage.pageIndex) - 1;
initFileList(localStorage.partA, localStorage.partB); } }); $(
"body").on("click", "#nextPageLi",function () { var totalPageCount =
localStorage.totalCount % localStorage.pageSize == 0 ? localStorage.totalCount
/ localStorage.pageSize : Math.ceil(localStorage.totalCount /
localStorage.pageSize);if (localStorage.pageIndex != totalPageCount) {
localStorage.pageIndex= parseInt(localStorage.pageIndex) + 1;
initFileList(localStorage.partA, localStorage.partB); } });function
formatDate(dt) {var date = new Date(dt); var aaaa = date.getFullYear(); var gg =
date.getDate();var mm = (date.getMonth() + 1); if (gg < 10) gg = "0" + gg; if
(mm < 10) mm = "0" + mm; var cur_day = aaaa + "-" + mm + "-" + gg; var hours =
date.getHours()var minutes = date.getMinutes() //var seconds =
date.getSeconds(); if (hours < 10) hours = "0" + hours; if (minutes < 10)
minutes = "0" + minutes; //if (seconds < 10) seconds = "0" + seconds; //return
cur_day + " " + hours + ":" + minutes + ":" + seconds; return cur_day + " " +
hours + ":" + minutes; } function getDistinctPartA() { return
JSLINQ(fileList).Distinct(function () { return this.PartA; }).items; } function
getDistinctPartB(partA) {return JSLINQ(fileList).Where(function () { return this
.PartA == partA; }).Distinct(function () { return this.PartB; }).items; }
function getFileListPage(partA, partB) { var totalPageCount =
localStorage.totalCount % localStorage.pageSize == 0 ? localStorage.totalCount
/ localStorage.pageSize : Math.ceil(localStorage.totalCount /
localStorage.pageSize);if (localStorage.pageIndex == 1) { $(
"#previousPageLi").addClass("disabled"); } else { $(
"#previousPageLi").removeClass("disabled"); } if (localStorage.pageIndex ==
totalPageCount) { $("#nextPageLi").addClass("disabled"); } else { $(
"#nextPageLi").removeClass("disabled"); } $("#pageIndexSpan"
).text(localStorage.pageIndex); $("#pageSizeSpan").text(localStorage.pageSize);
$("#totalCountSpan").text(localStorage.totalCount); if (partB == 'all') { return
JSLINQ(fileList).Reverse().Where(function () { return this.PartA == partA;
}).Skip(parseInt(localStorage.pageSize) * (parseInt(localStorage.pageIndex) -
1)).Take(parseInt(localStorage.pageSize)).Select("PartB,FileTime,FileSize,FileName"
).items; }else { return JSLINQ(fileList).Reverse().Where(function () { return
this.PartA == partA && this.PartB == partB;
}).Skip(parseInt(localStorage.pageSize) * (parseInt(localStorage.pageIndex) -
1)).Take(parseInt(localStorage.pageSize)).Select("PartB,FileTime,FileSize,FileName"
).items; } }function getFileListTotalCount(partA, partB) { if (partB == 'all') {
return JSLINQ(fileList).Count(function () { return this.PartA == partA; }); }
else { return JSLINQ(fileList).Count(function () { return this.PartA == partA &&
this.PartB == partB; }); } } function navBarLiClick(partA) { $("#navbar ul
li[data-value=\"" + partA +
"\"]").addClass("active").siblings().removeClass("active"); $("#nav ul"
).empty(); $("#nav ul").append("<li data-parta=\"" + partA + "\"
data-partb=\"all\"><a href='javascript:;'>all</a></li>"); var partB = ''; var
list = getDistinctPartB(partA); //list.sort(); list.reverse(); $.each(list,
function (i, v) { if (localStorage.partB != undefined && localStorage.partA !=
undefined && localStorage.partA == partA && localStorage.partB == v) { partB =
localStorage.partB; $("#nav ul").append("<li class='active' data-parta=\"" +
partA + "\" data-partb=\"" + v + "\"><a href='javascript:;'>" + v + "</a></li>"
); }else { $("#nav ul").append("<li data-parta=\"" + partA + "\" data-partb=\""
+ v + "\"><a href='javascript:;'>" + v + "</a></li>"); } }); if (partB == '') {
partB= 'all'; $("#nav ul li:first").addClass("active"); } initFileList(partA,
partB); }function navLiClick(partA, partB) { $("#nav ul li[data-partb=\"" +
partB + "\"]").addClass("active").siblings().removeClass("active");
initFileList(partA, partB); }function initFileList(partA, partB) { if
(localStorage.partA != partA || localStorage.partB != partB) {
localStorage.pageIndex= 1; localStorage.pageSize = 10; } if (localStorage.partA
!= partA) { localStorage.partA = partA; } if (localStorage.partB != partB) {
localStorage.partB= partB; } var totalCount = getFileListTotalCount(partA,
partB);if (localStorage.totalCount != totalCount) { localStorage.totalCount =
totalCount; }var fileList = getFileListPage(partA, partB); $("#table").empty();
//
$("#table").append("<thead><tr><th>FileName</th><th>Time</th><th>FileSize</th><th>Operate</th></tr></thead><tbody>");
if (fileList.length > 0) { $.each(fileList, function (i, v) { $(
"#table").append("<tr><td>" + v.FileName + "</td><td>" + v.FileTime +
"</td><td>" + v.FileSize + "</td><td><a href='" + v.FileName + "'
target='_blank'>Link</a></td></tr>"); }); } else { $("#table").append("<tr><td
colspan='4'><center>no data</center></td></tr>"); } $(
"#table").append("</tbody>"); } View Code

获取原页面里面的所有文件信息,项目名去重放在顶部,用Bootstrap的navBar插件展示,版本号由上到下依次放在左侧的nav插件上,右侧则用Bootstrap的table展示文件详细信息。效果如下



至此,分类、翻页展示功能完美实现,以后就算文件再多也可以快速找到了。

三、总结


通过“background”的方式注入,优点是页面随时可以配置,很方便,缺点是用户访问的每个页面都需要在“background.js”里面挨个过滤,感觉效率不行。个人还是比较喜欢“content_scripts”这种方式注入。

四、其他

Chrome插件开发有很多的api,功能非常强大。这里仅抛砖引玉,更多使用场景大家自己去发挥。源码地址
<https://github.com/oppoic/WebFileFilter>  Chrome商店(审核中)  开发文档
<https://developer.chrome.com/extensions/overview>

 

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信