上一篇文章 通过“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. Redis和memcached缓存技术

    缓存的定义:缓存就是在内存中存储的数据备份,当数据没有发生本质变化的时候,我们避免数据的查询操作直接连接数据库,而是去    内容中读取数据,这样就大大降低了数据库的读写次数,而且从内存中读数据的速度 ...

  2. 2.Spring 拦截器应用

    首先咱们来了解一下具体的业务场景(这个跟第一篇中的很相似但有不同):具体的业务是这样的,现在系统中有六十多个主档(功能模块),每个主档都有新增.修改.删除功能,当我们在对每个主档做这些操作时需要对其记 ...

  3. 运维工具pssh和pdsh安装和使用

    1. pssh安装与使用 1.1 pssh安装 [root@server]# wget http://peak.telecommunity.com/dist/ez_setup.py [root@ser ...

  4. Azure Active Directory document ---reading notes

    微软利用本地活动目录 Windows Server Active Directory 进行身份认证管理方面具有丰富的经验,现在这一优势已延伸基于云平台的Azure Active Directory.可 ...

  5. 【CF429E】 Points and Segments(欧拉回路)

    传送门 CodeForces 洛谷 Solution 考虑欧拉回路有一个性质. 如果把点抽出来搞成一条直线,路径看成区间覆盖,那么一个点从左往右被覆盖的次数等于从右往左被覆盖的次数. 发现这个性质和本 ...

  6. 【渗透攻防WEB篇】SQL注入攻击初级

    前言不管用什么语言编写的Web应用,它们都用一个共同点,具有交互性并且多数是数据库驱动.在网络中,数据库驱动的Web应用随处可见,由此而存在的SQL注入是影响企业运营且最具破坏性的漏洞之一,这里我想问 ...

  7. MySQL Schema与数据类型的优化

    选择优化的数据类型: 1. 更小的通常更好: 一般情况下,应该尽量使用可以正确存储数据的最小数据类型.更小的数据类型通常更快,因为他们占用更少的磁盘,内存和cpu缓存,并且处理时需要的cpu周期也更少 ...

  8. 新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析

    1.引言 Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件 ...

  9. Android JNI 学习(四):接口方法表 & Base Api & Exception Api

    本文我们来总结一下JNI 提供的功能列表及相关的函数表. 注意:请注意使用术语“必须”来描述对JNI程序员的限制.例如,当您看到某个JNI函数必须接收非NULL对象时,您有责任确保不将NULL传递给该 ...

  10. Eclipse 中构建 Maven 项目的完整过程 - 动态 Web 项目

    进行以下步骤的前提是你已经安装好本地maven库和eclipse中的maven插件了(有的eclipse中已经集成了maven插件) 一.Maven项目的新建 1.鼠标右键---->New--- ...