油猴腳本(Tampermonkey)是一個很是流行的瀏覽器擴展,它能夠運行由廣大社區編寫的擴展腳本,來實現各式各樣的功能,常見的去廣告、修改樣式文件、甚至是下載視頻。今天咱們就來看看如何編寫本身的油猴腳本。固然爲了運行油猴腳本,你應該在瀏覽器中安裝油猴插件。php
安裝油猴插件很是簡單,直接在瀏覽器的擴展商店中安裝便可。國產瀏覽器的話通常能夠經過下載擴展文件手動拖動的方式來安裝。下圖是微軟新版Edge瀏覽器的擴展商店,直接搜索Tampermonkey便可。css
首先在瀏覽器右上角找到並點擊油猴插件,選擇添加新腳本。html
而後就會打開如圖所示的編輯器窗口,咱們就能夠在其中編輯本身的腳本文件了。若是你喜歡的話,還能夠將腳本內容複製到合適的編輯器中編輯,完成以後再複製回來。node
若是你點擊開發者菜單的話,能夠選擇ES6模板,而後就能夠在腳本中使用新版JavaScript的特性了,它會有Babel轉譯回ES5。不過這個模板貌似有點問題,用了它就沒辦法使用代碼糾錯功能了。因此這裏我仍是選擇了默認的ES5模板。jquery
首先來看看腳本的內容,上面是一大排註釋,這些註釋能夠很是有用的,它代表了腳本的各個屬性。下面來簡單介紹一下。git
屬性名 | 做用 | |
---|---|---|
name | 油猴腳本的名字 | |
namespace | 命名空間,相似於Java的包名,用來區分相同名稱的腳本,通常寫成做者名字或者網址就能夠了 | |
version | 腳本版本,油猴腳本的更新會讀取這個版本號 | |
description | 描述,用來告訴用戶這個腳本是幹什麼用的 | |
author | 做者名字 | |
match | 只有匹配的網址纔會執行對應的腳本,例如* 、http://* 、http://www.baidu.com/* 等,參見谷歌開發者文檔 |
|
grant | 指定腳本運行所需權限,若是腳本擁有相應的權限,就能夠調用油猴擴展提供的API與瀏覽器進行交互。若是設置爲none 的話,則不使用沙箱環境,腳本會直接運行在網頁的環境中,這時候沒法使用大部分油猴擴展的API。若是不指定的話,油猴會默認添加幾個最經常使用的API |
|
require | 若是腳本依賴其餘js庫的話,可使用require指令,在運行腳本以前先加載其餘庫,常見用法是加載jquery | |
connect | 當用戶使用GM_xmlhttpRequest請求遠程數據的時候,須要使用connect指定容許訪問的域名,支持域名、子域名、IP地址以及* 通配符 |
|
updateURL | 腳本更新網址,當油猴擴展檢查更新的時候,會嘗試從這個網址下載腳本,而後比對版本號確認是否更新 |
下面簡單介紹一下grant指令那裏能夠填寫的一些權限,詳情請查看油猴腳本文檔。這裏就簡單介紹幾個經常使用的,能夠調用的函數所有以GM_做爲開頭。github
權限名 | 功能 |
---|---|
unsafeWindow | 容許腳本能夠完整訪問原始頁面,包括原始頁面的腳本和變量。 |
GM_getValue(name,defaultValue) | 從油猴擴展的存儲中訪問數據。能夠設置默認值,在沒成功獲取到數據的時候當作初始值。若是保存的是日期等類型的話,取出來的數據會變成文本,須要本身轉換一下。 |
GM_setValue(name,value) | 將數據保存到存儲中 |
GM_xmlhttpRequest(details) | 異步訪問網頁數據的API,這個方法比較複雜,有大量參數和回調,詳情請參考官方文檔。 |
GM_setClipboard(data, info) | 將數據複製到剪貼板中,第一個參數是要複製的數據,第二個參數是MIME類型,用於指定複製的數據類型。 |
GM_log(message) | 將日誌打印到控制檯中,可使用F12開發者工具查看。 |
GM_addStyle(css) | 像網頁中添加本身的樣式表。 |
GM_notification(details, ondone), GM_notification(text, title, image, onclick) | 設置網頁通知,請參考文檔獲取用法。 |
GM_openInTab(url, loadInBackground) | 在瀏覽器中打開網頁,能夠設置是否在後臺打開等幾個選項 |
還有一些API沒有介紹,請你們直接查看官方文檔吧。ajax
編寫腳本就很簡單了,編寫到// Your code here ..
那裏便可。能夠編寫函數,而後在最後調用這幾個函數,這樣的模塊化編寫方法寫出來的腳本比較容易維護。chrome
前段時間瞭解了vagrant這個東西,感受頗有意思,準備研究一下,可是照着官網教程運行的時候,第一步就發生了錯誤。我上網一搜,原來我更新的virtualbox比較新,vagrant剛好不支持。可是現在幾個月過去了,vagrant仍是沒有更新,因此我要寫一個腳本,等到vagrant更新的時候,給我網頁上彈出一個對話框。segmentfault
首先訪問vagrant官網,而後就能夠看到中間下載按鈕上大大的版本號2.2.6了。由於版本確定是不會倒退的,因此只要判斷一下版本號不是2.2.6,就能夠彈出提示了。經過F12開發者工具能夠看到,這三個按鈕其實都是連接,只不過顯示成了按鈕的樣子,並且他們剛好都位於header
標籤之中。若是若是能夠的話,直接用選擇器就能夠很是輕鬆的獲取到版本號。
爲了能在更新的時候及時獲取到提示,我須要腳本在全部網站上生效,來檢測版本。可是這樣作會致使另一個問題,那就是每次打開一個網頁都會運行一次檢查vagrant的腳本,而這是徹底沒必要要的。因此須要一個額外的判斷,這就須要利用油猴提供的API來保存當前日期,只有天天第一次的時候纔會執行檢查代碼。原本我想的很複雜,須要一個日期變量,而後還要額外一個變量保存是不是今天第一次更新。後來我發現我想的太多了,作法其實很簡單。天天先獲取一第二天期,而後和事先保存的日期比較,若是不同的話才執行腳本,並將日期設置爲今天的日期;若是日期同樣的話無事發生。
最後一個問題就是如何來判斷版本號,有兩種方法:第一種就是上面提到的,直接解析HTML代碼並找到版本號;第二種是更直接的辦法, 由於vagrant也是Github上開源的項目,因此能夠直接調用Github的API來獲取最新發布的版本號。惋惜的是,第二種辦法我試了一下竟然不成功,不知爲什麼,沒辦法獲取到發佈信息,可是換成其餘項目就能夠。因此最後沒辦法只好採用第一種辦法。有興趣的同窗能夠本身試一下第二種方法。
好了,全部相關的坑我都已經解釋完畢了,相信你們應該很容易就能夠看懂下面的代碼,我就不介紹了。雖然看着簡單,可是我其實仍是踩了很多的坑,就這點代碼花了我好幾天的時間。並且確實這個代碼寫的也並非很好,由於ajax取回來的代碼是完整一個html頁面,貌似用原版DOM API沒辦法解析,最後只好用jQuery的parseHTML
方法解析的。並且我還由於原生方法和jQuery之間的方法名搞混了,浪費了不少時間。
// ==UserScript== // @name remind_me_vagrant_update // @namespace https://github.com/techstay/myscripts // @version 0.1 // @description remind me if vagrant support virtualbox // @author techstay // @match * // @require https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js // @connect vagrantup.com // @grant GM_setValue // @grant GM_getValue // @grant GM_setClipboard // @grant GM_log // @grant GM_xmlhttpRequest // @grant unsafeWindow // @grant window.close // @grant window.focus // ==/UserScript== (function () { 'use strict'; const CHECKED_DATE = 'checkedDate'; function checkDateEquals(a, b) { return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); } function checkVagrantVersion() { GM_setValue(CHECKED_DATE, new Date()); GM_xmlhttpRequest({ "method": "GET", "url": "https://www.vagrantup.com/", "headers": { "user-agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36' }, "onload": function (result) { var list = jQuery.parseHTML(result.response); jQuery.each(list, function (i, el) { if (el.nodeName == 'HEADER') { var header = jQuery.parseXML(el.innerHTML); var version = header.getElementsByTagName('a')[1].textContent.replace('Download ', ''); if (version != '2.2.6') { alert('Vagrant update!'); } return false; } }); } }); } var today = new Date(); var lastCheckedDay = new Date(GM_getValue(CHECKED_DATE, new Date('2006-1-1'))); if (!checkDateEquals(lastCheckedDay, today)) { checkVagrantVersion(); } })();
編寫腳本很難一次成功,大部分時間都花在了調試上面。調試油猴腳本的話有幾種調試方法。
第一種方法就是最原始的打印日誌,能夠利用console.log
和GM_log
來將關鍵信息打印出來,上面的腳本就是我靠打印日誌一點點發現各類參數錯誤的。說實話這種辦法有點笨。
第二種就是利用瀏覽器的調試功能,在腳本須要調試的地方插入debugger;
語句,而後在打開F12開發者工具的狀況下刷新頁面,就會發現網頁已經暫停在相應位置上。這樣就能夠利用F12開發者工具進行單步調試、監視變量等操做了。
個人文章通常都是簡書首發,而後複製粘貼到Csdn中,可是後來我發現每次手動操做太蠢了,爲何不用腳原本自動化呢?因此我又寫了個腳本幫忙完成自動化工做。原本覺得這個腳本應該比較簡單,不過仍是踩了不少坑才湊合把功能寫出來。
首先是數據的保存,利用油猴提供的GM_setValue
卻是能夠很簡單的將文章標題和內容保存起來。不過問題來了,如何在不一樣頁面之間共享呢?有幾種方案:第一種最簡單粗暴,直接複製兩份,對應頁面首先判斷是否存在數據,存在的話才執行復制操做,而後清空數據。這種方案最簡單,並且若是本身直接新建文章的話也不會出問題。第二種就是數據只保存一份,經過幾個變量來肯定何時複製完成,清空數據,可是這樣比較複雜,要理清邏輯順序很麻煩。因此最後我就採用了第一種辦法。
而後又遇到一個問題,那就是若是編輯器自帶了保存和恢復功能,極可能會把我複製過去的文章給覆蓋了,因此須要等頁面加載完以後,延遲一段時間才進行復制操做。而後我又谷歌了一番,差很少解決了這個問題。
而後遇到了一個很是棘手的問題,就是SF的編輯器設計比較複雜,沒辦法經過直接填充value
或者text
屬性的方式來寫入文章,我想了好久也沒有想出來怎麼解決。沒辦法只好改用剪貼板的方式來糊弄了,也就是將文章內容複製到剪貼板裏頭,而後手動粘貼到編輯器中。
最後一個問題就是簡書上這個複製按鈕應該如何實現,其實簡書編輯器的工具欄卻是空了一些部分,我原本想把按鈕直接加到那個上面。可是我發現貌似一旦添加東西,那個工具欄會自動重載取消更改,因此水平所限沒作到,只好利用jQueryUI加了一個很醜的浮動按鈕,並且由於拖動的時候會觸發單擊,沒辦法把按鈕改爲了雙擊觸發。
最後的腳本就是下面這樣的。相比第一個腳本多了幾個打開新頁面、刪除變量、訪問剪貼板的API。
// ==UserScript== // @name copy_jianshu_to_csdn_and_segmentfault // @namespace https://github.com/techstay/myscripts // @version 0.1 // @description 將簡書文章複製到csdn和思否編輯器中 // @author techstay // @match https://editor.csdn.net/md/ // @match https://segmentfault.com/write // @match https://www.jianshu.com/writer* // @require https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js // @require https://cdn.bootcss.com/jqueryui/1.12.1/jquery-ui.min.js // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant unsafeWindow // @grant GM_setClipboard // @grant window.close // @grant window.focus // @grant GM_openInTab // ==/UserScript== (function () { 'use strict'; const SF_URL = 'https://segmentfault.com/write' const CSDN_URL = 'https://editor.csdn.net/md/' const SF_TITLE = 'sf_title' const SF_CONTENT = 'sf_content' const CSDN_TITLE = 'csdn_title' const CSDN_CONTENT = 'csdn_content' function saveArticle() { GM_setValue(CSDN_TITLE, $('._24i7u').val()) GM_setValue(CSDN_CONTENT, $('#arthur-editor').val()) GM_setValue(SF_TITLE, $('._24i7u').val()) GM_setValue(SF_CONTENT, $('#arthur-editor').val()) } function copyToCsdn() { var title = GM_getValue(CSDN_TITLE, '') var content = GM_getValue(CSDN_CONTENT, '') if (title != '' && content != '') { $('.article-bar__title').delay(2000).queue(function () { $('.article-bar__title').val(title) $('.editor__inner').text(content) GM_deleteValue(CSDN_TITLE) GM_deleteValue(CSDN_CONTENT) $(this).dequeue() }) } } function copyToSegmentFault() { $(document).ready(function () { var title = GM_getValue(SF_TITLE, '') var content = GM_getValue(SF_CONTENT, '') if (title != '' && content != '') { $('#title').delay(2000).queue(function () { $('#title').val(title) GM_setClipboard(content, 'text') GM_deleteValue(SF_TITLE) GM_deleteValue(SF_CONTENT) $(this).dequeue() }) } }) } function addCopyButton() { $('body').append('<div id="copyToCS">雙擊複製到CSDN和思否</div>') $('#copyToCS').css('width', '200px') $('#copyToCS').css('position', 'absolute') $('#copyToCS').css('top', '70px') $('#copyToCS').css('left', '350px') $('#copyToCS').css('background-color', '#28a745') $('#copyToCS').css('color', 'white') $('#copyToCS').css('font-size', 'large') $('#copyToCS').css('z-index', 100) $('#copyToCS').css('border-radius', '25px') $('#copyToCS').css('text-align', 'center') $('#copyToCS').dblclick(function () { saveArticle() GM_openInTab(SF_URL, true) GM_openInTab(CSDN_URL, true) }) $('#copyToCS').draggable() } $(document).ready(function () { if (window.location.href.startsWith('https://www.jianshu.com')) { addCopyButton() } else if (window.location.href.startsWith(SF_URL)) { copyToSegmentFault() } else if (window.location.href.startsWith(CSDN_URL)) { copyToCsdn() } }) })()
踩了幾天坑,最後總結一下編寫油猴腳本的一點步驟。首先要思考腳本的實現方式,須要用到什麼API和權限,而後填寫好腳本的註釋信息。
而後將功能封裝成函數的形式,最後在腳本末尾調用實現的函數。寫的差很少的時候複製到瀏覽器中嘗試運行。
遇到困難的時候,可能須要直接在F12開發者工具裏進行調試。有些網頁不用jQuery,爲了方便,咱們須要本身將jQuery導入到頁面中,能夠將下面的代碼複製到瀏覽器控制檯中。
var jq = document.createElement('script'); jq.src = "https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js"; document.getElementsByTagName('head')[0].appendChild(jq);
腳本作完了,天然是要共享出來讓你們一塊兒使用的。固然既然要發佈,天然要支持更新方便往後維護。方法也很簡單,直接在上面的註釋部分添加updateURL
便可,而後設置腳本訪問地址。例如我要將腳本發佈到Github上,就添加下面的註釋。
// @updateURL https://raw.githubusercontent.com/techstay/myscripts/master/tampermonkey/remind_me_vagrant_update.js
油猴腳本支持好幾個網站,其中目前最主流的是GreasyFork,登陸這個網站註冊一個帳號,而後進入用戶頁面選擇提交腳本,而後填寫腳本代碼和各項信息。
這樣腳本就提交上去了,其餘人也能夠搜索到並安裝腳本了!