第 1 章 开始

1.1. Greasemonkey 是什么?

Greasemonkey 是一个跨越chrome、firefox的扩展,它具有通过编写脚本来改变被访问网页的功能。 

可以: 

1. 使访问的网站更便于阅读或者更便于使用; 

2. 修复网页渲染的缺陷,而无须烦扰网站管理员。 

3. 让网页更好地使用残疾人援助技术,清楚响亮地说出网页内容,或者将网页内容变为盲文。 

4. 自动地获得其它网站的数据,从而使两个网站更好地相互链接起来。

然而 Greasemonkey 本身并没有作这些事。实际上,在您安装它之后,您注意到根本没有任何变动…直到您开始安装一种叫做“用户脚本”的东西。用户脚本(user script)就是一大块 Javascript 代码,还有些附加信息,用来告诉 Greasemonkey 脚本应该在何时何地运行。每个用户脚本能够针对具体页面,具体网站,或者一批网站。用户脚本能做到您在 Javascript中可做到的任何事情。实际上,它能做得更多,因为 Greasemonkey 提供了专供用户脚本使用的函数。

这里是Greasemonkey 脚本库含了上百个用户脚本,这些都是用户为了满足自己的需要而写的。一旦您写了自己的用户脚本,只要您认为别人也许发现它有用,您可以把它添加到脚本库中。您也可以自己使用,因为从编写过程中获得知识,获得满足感,才是更重要的。

目前国内主要的油猴脚本网站是:​​Greasy Fork​

1.2. 安装 Greasemonkey

要使用用户脚本,您首先需要安装一个用户脚本管理器。根据您使用的浏览器不同,可用的用户脚本管理器也有所不同。

我使用的是chrome浏览器,安装的是​​Tampermonkey​​​ 。安装过程可能需要FQ,我是在​​chrome的网上应用商店​​直接下的。

其他浏览器,请参考:​​深入浅出 Greasemonkey​

1.3. 安装用户脚本

Greasemonkey “用户脚本”是用 Javascript 编写的独立文件,用来定制一个或多个网页。


[提示] 您可以在Greasemonkey脚本库,找到许多用户脚本。尽管没人要求您必须把脚本放到那儿去,但是实际上,您可以把您的脚本共享到任何地方,这样别人就可以安装它了。甚至您根本不需要一台网络服务器,因为你可以从本地文件中安装用户脚本。


用户脚本的文件名必须以.user.js结尾。 

 

鼠标左键点击油猴图标,出现如图所示界面,其中“网页限制解除”、“DownloadAllContent”等等,都是之前添加的油猴脚本。 

添加新脚本的时候,点击“添加新脚本”,弹出 新建用户脚本 编辑界面。 

油猴安装、编写及添加脚本 笔记_javascript

界面内编辑器里,可以添加脚本,现在出现的是默认设置。 

点击获取新脚本,转到​​油猴脚本库​​,可以从中选择合适的油猴脚本。 

油猴安装、编写及添加脚本 笔记_chrome_02

参考资料

​Greasemonkey 脚本库​​有上百个 Greasemonkey 脚本。

2. 编辑用户脚本

如果愿意可以安装很多个 Greasemonkey 脚本。 Greasemonkey 带有配置对话框来管理用户脚本:暂时禁用,改变配置或卸载脚本。 

油猴安装、编写及添加脚本 笔记_chrome_03 

on/off 可以选择启用或暂停脚本。 

点击管理面板,可以对脚本进行管理,编辑,卸载 

油猴安装、编写及添加脚本 笔记_元数据_04

管理比较简单,一看就懂。

第 2 章 编写第一个用户脚本

2.1. Hello World

我们步入 Greasemonkey 美妙世界的万里长征将从第一步开始,所有读过技术手册的读者都会很熟悉这一步:让您的电脑打出“Hello world”。

例 2.1. helloworld.user.js

// Hello World! example user script
// version 0.1 BETA!
// 2005-04-22
// Copyright (c) 2005, Mark Pilgrim
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
//
// --------------------------------------------------------------------
//
// This is a Greasemonkey user script.
//
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// To uninstall, go to Tools/Manage User Scripts,
// select "Hello World", and click Uninstall.
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name Hello World
// @namespace http://diveintogreasemonkey.org/download/
// @description example script to alert "Hello world!" on every page
// @include *
// @exclude http://diveintogreasemonkey.org/*
// @exclude http://www.diveintogreasemonkey.org/*
// ==/UserScript==
alert('Hello world!');

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

正如您所见到的,这个Hello World脚本的大部分都是注释。有些注释,比如如何安装,没什么特殊含义;那只是对初学者的一些指导。但是,有一节注释确实有特殊含义,下一节会有详细的解释。

要看到脚本的效果,您首先要安装,然后访问一个不在diveintogreasemonkey.org域名下的网站(例如,Google)。这个页面将会像平时一样显示出来,还会弹出一个对话框:“Hello world!”

下载

2.2. 用元数据描述您的用户脚本

每个用户脚本都含有一段元数据,用来向 Greasemonkey 描述这个脚本自身的信息:发行者,执行规则等等。

例 2.2. Hello World 元数据

// ==UserScript==
// @name Hello World
// @namespace http://diveintogreasemonkey.org/download/
// @description example script to alert "Hello world!" on every page
// @include *
// @exclude http://diveintogreasemonkey.org/*
// @exclude http://www.diveintogreasemonkey.org/*
// ==/UserScript==

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里有六条独立的元数据信息,作为一个整体包含在注释中。现在让我们按顺序逐条解释。首先讲最外面的这层包装。

// ==UserScript==
//
// ==/UserScript==

  • 1
  • 2
  • 3

上述标记很重要,必须完全吻合。Greasemonkey 用它们来标记用户脚本的元数据段。这段注释可以放在用户脚本的任何部位,但经常会放在靠近顶部的地方。 

在元数据段内,第一项是名字。

// @name          Hello World

  • 1

这是您的用户脚本的名字。它将会在您第一次安装脚本时在安装对话框(install dialog)中显示出来。之后会显示在“管理用户脚本”对话框中。这个名字应该言简意赅。

@name可选的。如果存在,它只能被定义一次。如果不存在,将会默认显示用户脚本的去掉扩展名.user.js的文件名。

下一个是命名空间(namespace)。

// @namespace     http://diveintogreasemonkey.org/download/

  • 1

这是一个 URL,Greasemonkey 用它来区分名称相同但是作者不同的用户脚本。如果您有一个域名,您可以使用它作命名空间。另外您也可以用 ​​tag: URI​​。

@namespace是可选的。如果存在,它只能被定义一次。如果不存在,将会默认使用下载用户脚本的网站域名。

元数据可以以任意次序排列。推荐使用@name,@namespace,@description,@include,最后是@exclude,但是其它的顺序也没关系。

下一项是描述。

// @description   example script to alert "Hello world!" on every page

  • 1

这是关于用户脚本功能的描述。在您第一次安装脚本时,它将会在安装对话框中显示,之后会在“管理用户脚本”对话框中显示。描述不应多于两句。

@description 是可选的。如果使用它,那么它只能被定义一次。如果不使用,默认会显示为空白。

[重要] 

不要忘记写@description。即使您所写的用户脚本是给自己用的。你最后很可能会拥有很多脚本,如果没有描述的话,在“管理用户脚本”对话框中管理脚本将会成为一件令人头疼的事。

下面三行是最重要的 (从 Greasemonkey 的角度来看):@include和@exclude URL。

// @include       *
// @exclude http://diveintogreasemonkey.org/*
// @exclude http://www.diveintogreasemonkey.org/*

  • 1
  • 2
  • 3

这几行让 Greasemonkey 知道在那些网站上执行您的用户脚本。您可以明确的指定一个 URL,或者用通配符 * 来代替域名或路径中的部分字符。在这个例子中,我们告诉 Greasemonkey 在除了 ​​http://diveintogreasemonkey.org/​​​ 和 ​​http://www.diveintogreasemonkey.org/​​​ 的所有网站上执行。排除(Excludes)优先于包含(includes),所以即使 ​​http://diveintogreasemonkey.org/download/​​​ 匹配 * (所有网站),它还是会被排除掉,因为它还匹配 ​​http://diveintogreasemonkey.org/​​* 。

@include和 @exclude 是可选的,可以自定义执行和豁免的 URL,但必须每条规则各占一行。如果您没有任何定义, Greasemonkey 将会对所有的网站执行您的用户脚本。(等同于 @include *)。


[注意] 
您需要定义非常精确的@include和@exclude元数据。Greasemonkey 不会对域名作任何的假设,如果一个网站符合​​http://example.com/​​和​​http://www.example.com/​​,您需要把这两个网址都标示出来。


参考资料

2.3. 编写用户脚本代码

我们的第一个用户脚本是在执行时简单地显示一条提示信息:“Hello world!”。

例 2.3. 显示“Hello world!”提示信息

alert('Hello world!');

  • 1

尽管这段代码仿佛够用了,而且也达到了目的。Greasemonkey 实际上在幕后做了很多的事情来确保用户脚本不会与页面所包含的原有脚本发生严重的冲突。特别是它会自动的把您的用户脚本封装在一个匿名的函数包里。一般情况下,您可以忽视,但是终究有一天会让您遇到麻烦。所以最好现在就了解一下。

最经常遇到的麻烦之一是在用户脚本里定义的变量和函数不能被别的脚本访问。事实上,只要用户脚本运行完了,所有的变量和函数就都不能使用了。如果您期望使用 window.setTimeout 函数,或者在链接挂上字符串式的 onclick 属性然后期望 Javascript 稍后调用您的函数,那么您会遇到问题。

例如,下面这个用户脚本中定义了一个函数helloworld, 然后尝试设置一个计数器来在一秒后调用这个函数。

例 2.4. 延迟调用函数的错误方法

function helloworld() {
alert('Hello world!');
}
window.setTimeout("helloworld()", 60);

  • 1
  • 2
  • 3
  • 4

这段代码没有起任何作用;不会弹出提示窗口。如果您打开错误控制台,会看到一个异常:Error: helloworld is not defined.这是因为当延迟结束,开始调用helloworld()时,helloworld函数已经不存在了。

如果您需要引用用户脚本中的变量或者函数,应该显式的把它们定义为window对象的属性,它是始终存在的。

例 2.5. 延迟调用函数的更好方法

window.helloworld = function() {
alert('Hello world!');
}

window.setTimeout("helloworld()", 60);

  • 1
  • 2
  • 3
  • 4
  • 5

目的达到了!页面完成加载一秒后,一个提示框骄傲的弹了出来,写着:“Hello world!”

然而,在 window上设置属性依然不太理想;这有点像用全局变量来做局部变量该做的事。(事实上,就是那么回事,window是全局的,可以被页面中的所有脚本访问。更实际的讲,它可能会与页面自身的脚本,甚至是其它的用户脚本相互干扰。

最佳的解决方案是定义匿名函数,把它作为第一个参数传递给 window.setTimeout。

例 2.6. 延迟调用函数的最好方法

window.setTimeout(function() { alert('Hello world!') }, 60);

  • 1

我在这里所做的是建立一个没有名字的函数(一个“匿名函数”),然后直接把它传递给 window.setTimeout。这样可以完成与上个例子相同的事,而不会留下痕迹。例如不会被其它的脚本检测到。

我发现我在写用户脚本时经常使用匿名函数。它们很适合创建“一次性”函数,然后当作参数传递给类似window.setTimeout,document.addEventListener 或者赋值给事件句柄像 click 或 submit。

参考资料


  1. ​Javascript 中的匿名函数​
  2. ​Block Scope in Javascript​
  3. ​associated discussion thread​

2.4. 保存用户脚本

对于脚本的作者来讲,“管理用户脚本”对话框有个很实用的功能:编辑按钮可以“动态的(live)”修改已安装的脚本。 

修改/编辑完脚本文件后,点击保存按钮,脚本文件就自动加载到油猴里面。 

油猴安装、编写及添加脚本 笔记_javascript_05 

这时候打开新网页,就会弹出hello world对话框。 

油猴安装、编写及添加脚本 笔记_chrome_06

第 3 章 修改网页操作

在chrome上编辑油猴脚本的时候,可以按F12键,调出chrome的控制台帮助调试,一些错误命令会在控制台上显示出来,比较方便。

油猴脚本库中,有一个下载小说的脚本,我觉得对我这种重度小说依赖症来说,是个福音。因此,我练手的时候,打算以此为蓝本,练习自己的代码。计划是这样:在小说网站的网页上添加一个下载小说的按钮,如果想下载小说,可以按此按钮一键下载。

首先,先看一下,如何在页面上添加一个按钮。

3.1 给网页添加一个按钮

这个脚本用作练习,功能是在网页上指定位置添加一个按钮。

脚本如下:

// ==UserScript==
// @name Hello World_cjn2
// @namespace
// @description
// @include https://www.bixia.org/*
// @include https://bixia.org/*
// ==/UserScript==
(function() {
'use strict';
var button = document.createElement("input"); //创建一个input对象(提示框按钮)
button.setAttribute("type", "button");
button.setAttribute("value", "下载");
button.style.width = "60px";
button.style.align = "center";
button.style.marginLeft = "250px";
button.style.marginBottom = "10px";
button.style.background = "#b46300";
button.style.border = "1px solid " + "#b46300";//52
button.style.color = "white";
var x = document.getElementById("maininfo");
x.appendChild(button);
// Your code here...
})();

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

效果如下,加载完页面后,会在画圈的位置添加一个按钮: 

油猴安装、编写及添加脚本 笔记_调用函数_07

代码的前半部分,都是用来给按钮添加各种属性。后半部分指明了添加在网页中的位置,这里给元素节点 maininfo 追加一个子节点 button

var x = document.getElementById("maininfo");
x.appendChild(button);

  • 1
  • 2

有时候,调试不成功要注意查看控制台输出错误,看看哪里不对。