Greasemonkey 最初只是 Firefox 的一个扩展,不过流行之后,很快被其它的浏览器以不同形式采纳。IE 我不喜欢,此文就不关心它了。剩下的市场份额较高的是 Chrome 和 Firefox, 本文就简单讨论一下如何写出同时支持这两种浏览器的 user script (所谓“跨浏览器”).
最早的时候也是热心用户给 Chrome 写了插件让它支持 user scripts, 但是现在 Chrome 不再另外需要插件就可以支持了。它的处理方式是每次安装 user script 的时候,自动把它转换成一个扩展!刚开始在 Chrome 上调试脚本的时候,我还尝试在磁盘上找到它存储脚本的目录——在 Firefox 里我都是直接编辑脚本,保存,刷新页面马上看到效果,简单粗暴。肯定有更简单的调试方式,只是我不知道。
看看 Chrome 的官方文档怎么介绍的:
Chromium does not support @require, @resource, unsafeWindow, GM_registerMenuCommand, GM_setValue, or GM_getValue.
GM_xmlhttpRequest is same-origin only.
这个显然有点过时。根据最近被完成的这个 issue,现在 GM_xmlhttpRequest 已经可以跨域请求了。
关键的一点,Chrome 不支持 @require 的写法,这可是个很方便的功能啊。我对 jQuery 相对熟悉一点,几乎每个脚本里都会用这个命令把 jQuery 引入。这里有个例子介绍怎么解决这个问题。再稍稍加工一下,就得到一个跨浏览器的引入多个外部 JavaScript 的方案:
跨浏览器 @require // ==UserScript== // @name ??? // @namespace http://your.tld/ // ==/UserScript== var scripts = [ '//cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js', '//www.readability.com/embed.js' ]; var numScripts = scripts.length, loadedScripts = 0; GM_addStyle('CSS styles goes here'); function main() { jQuery.noConflict(); // if window.$ has been used by other libs // ... } var i, protocol = document.location.protocol; for (i = 0; i < numScripts; i++) { var script = document.createElement("script"); script.setAttribute("src", protocol + scripts[i]); script.addEventListener('load', function() { loadedScripts += 1; if (loadedScripts < numScripts) { return; } var script = document.createElement("script"); script.textContent = "(" + main.toString() + ")();"; document.body.appendChild(script); }, false); document.body.appendChild(script); console.log(script); } 在这个方案里,我把主要的逻辑都放在 main 函数里。需要搞清楚的是,main 不会在这个扩展脚本的 scope 里运行!它实际上是作为页面里内嵌的一段 JavaScript 代码被执行了,所以不要在里面引用任何所谓“全局变量”(不过可以引用你所确定知道的页面 window 对象的属性)。所以它不能调用 GM_addStyle, 于是我放在 main 外面。需要 Ajax 的话,也只能使用普通的 XMLHttpRequest 对象,也就不能进行跨域数据请求了,这在某些脚本里可能无法忍受。
...