上一篇文章 通过“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的配置参考:官方文档  看看现在的项目结构

一个简单的没有任何功能的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

现在来看下官方说的可以常驻后台运行的“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>&nbsp;/&nbsp;<span id="pageSizeSpan"></span>&nbsp;/&nbsp;<span id="totalCountSpan"></span>&nbsp;&nbsp;<li id="previousPageLi"><a href="javascript:;">Prev</a></li>&nbsp;<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>");
}

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

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

三、总结

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

Chrome插件本地源码路径,方便学习其他优秀插件的代码:

1)windows xp:C:\Documents and Settings\用户名\Local Settings\Application Data\Google\Chrome\User Data\Default\Extensions
2)windows xp+:C:\Users\用户名\AppData\Local\Google\Chrome\User Data\Default\Extensions
3)MAC:~/Library/Application Support/Google/Chrome/Default/Extensions
4)Ubuntu:~/.config/google-chrome/Default/Extensions

四、其他

Chrome插件开发有很多的api,功能非常强大。这里仅抛砖引玉,更多使用场景大家自己去发挥。源码地址  Chrome商店  开发文档

Chrome插件开发,美化网页上的文件列表。chrome-extension,background的更多相关文章

  1. Chrome插件开发,美化网页上的文件列表。chrome-extension,content-scripts

    趁着2018年还剩最后几天,发几篇博客,荒废太久了,惭愧. 最近也是需求驱动,研究了下Chrome插件开发.来看一下我们公司运维提供的日志查看页面 所有项目的日志都参杂在一起,每次去找都很痛苦.慢慢发 ...

  2. Juploader 1.0 谷歌(chrome)浏览器中成功上传文件后返回信息异常

    在项目中使用了Juploader 1.0无刷新上传文件的js组件,在IE8以上没有问题,代码如下: function InitialUploadDirectly(OnUploadFunc, butto ...

  3. 基于Apache服务在centos7上搭建文件列表

    参考文献: https://www.cnblogs.com/snake553/p/8856729.html https://blog.csdn.net/yejinxiong001/article/de ...

  4. php网页上传文件到Ubuntu服务器(input type=fire)- 赖大大

    直接上代码: <form enctype="multipart/form-data" method="post" action=""& ...

  5. 【已解决】ERR_BLOCKED_BY_XSS_AUDITOR:Chrome 在此网页上检测到了异常代码:解决办法

    工作中,用Selenium自动化填表并获取结果时,程序一直安静的读取数据库,网页填表,获取结果,存库,但跑着跑着突然报错了. 排查后,原来不是Selenium的问题,是数据比较特殊,带了个双引号,如下 ...

  6. linux下载网页上的文件夹以及删除文件(stream)

    wget -nd -r -l1 --no-parent http://www.cs.virginia.edu/stream/FTP/Code/ 注:-nd 不创建目录:-r 递归下载:-l1只下载当前 ...

  7. 网页上下载文件提示框(vb.net)

    Public Sub downLoadFile(ByVal fPath As String) Dim fileInfo As System.IO.FileInfo = New System.IO.Fi ...

  8. IIS发布的网页上传文件被拒绝

    在IIS所在的服务器共享的权限(如下图示,但注意不是加everyone)和共享文件夹的权限里都加上IIS_USER完全控制,如果不行再加上NETWORK SERVICE权限

  9. C#WPF做FTP上传下载获取文件列表

    Xaml.cs: using Microsoft.Win32;using System;using System.Collections.Generic;using System.IO;using S ...

随机推荐

  1. Exception.ToString()使用及其他方法比较

    在日常C#的编码过程中,我们常常会使用try...catch...来抓住代码异常,并且在异常的时候打印log, 如下 try { } catch (Exception e) { //输出Log信息等 ...

  2. python学习,excel操作之xlsxwriter常用操作

    from datetime import datetime import xlsxwriter #打开文件 workbook = xlsxwriter.Workbook('Expenses03.xls ...

  3. 急速安装lnmp 编译版本

    急速安装lnmp 编译版本 安装msyql+PHP 系统centos6.5 安装 开发软件包 已经改成了163的源需要执行下面的代码 官网不自带 libmcrypt libmcrypt-devel w ...

  4. 搭建vue环境

    1. 下载安装nodejs 截至2018-06-05 最新稳定版本为 8.11.2,直接 next ,不改目录. PS C:\Users\Administrator> node -v v8.11 ...

  5. django查询数据库无法过滤月份的解决

    我试过,当settings里的:USE_TZ = False时也可以查询,但是数据库里的时间就会显示错的 解决方法是可以再终端输入 mysql_tzinfo_to_sql /usr/share/zon ...

  6. Linux tgtadm: Setup iSCSI Target ( SAN )

    Linux target framework (tgt) aims to simplify various SCSI target driver (iSCSI, Fibre Channel, SRP, ...

  7. Virtual Networking

    How the virtual networks used by guests work Networking using libvirt is generally fairly simple, an ...

  8. 【java】java反射初探 ——“当类也学会照镜子”

    反射的作用   开门见山地说说反射的作用   1.为我们提供了全面的分析类信息的能力 2.动态加载类   我理解的“反射”的意义 (仅个人理解哈)   我理解的java反射机制就是: 提供一套完善而强 ...

  9. Linux基本命令-ls

    ls 作用:显示目标列表,在Linux中是使用率较高的命令.ls命令的输出信息可以进行彩色加亮显示,以分区不同类型的文件. 参数: -a:显示所有档案及目录(ls内定将档案名或目录名称为“.”的视为影 ...

  10. FloatingWindow 悬浮窗开源项目总结

    在Android开发中,我们不免会遇到悬浮窗展示的需求,以下是本人之前star的悬浮窗的开源项目,供大家参考: 一.FloatingWindowDemo 开源项目地址:https://github.c ...