不用寫外掛也能惡搞網頁
有時候是不是覺得瀏覽器很智障?網頁很不人性? 要是網頁是自己設計的就好了? 除了寫超爆幹複雜的 plug-in 外, 還有方便的 greasemonkey,和無腦的 bookmarklet, 都可以達到類似的效果。
- 油猴 Greasemonkey
- 小書籤 bookmarklet
- 開發者工具 inspect
活動介紹
本次 gholk 要來分享上古時代的瀏覽器密技 小書籤 , 和簡單粗勇的 油猴腳本 , 讓 javascript 開發者再也不用在 F12 開發者工具裡瞎忙半天, 也不需要寫超爆幹複雜的瀏覽器外掛, 就能輕鬆惡搞網站。
ccca 活動介紹
【社課 / Activity】
學過網頁設計的你,是否曾對設計不良的網頁感到不滿? 除了按 F12 使用瀏覽器的開發者工具,手動鍵入 js 來修改網頁外, 你還知道有小書籤 (bookmarklet) 、油猴 (greasemonkey) 腳本 (*.user.js) , 可以用來達成各式各樣的功能嗎?
當然,附加元件(瀏覽器外掛)問世後, 很快以更多的功能、更全面的權限管理,站上了這一系列工具的頂端。 但無論如何,如果你常常在開發者工具對第三方網頁敲敲打打, 那你絕對會喜歡小書籤與油猴腳本的。
<不用寫外掛也能惡搞網頁 - 小書籤與油猴腳本簡介>
社課中會介紹開發者工具、小書籤、油猴腳本, 與開發、發布程式上的技巧。
- 對象:會寫 javascript 的人
- 講者:gholk
- 時間:2019-11-28 19:00-21:00
- 地點:網路資訊服務中心 訓練教室(請由小木屋鬆餅側自動門出入)
- 聊天室:https://t.me/ccca_chat
簡報模式
切換樣式
顯示小書籤程式碼
翻頁按鈕
把按鈕移到螢幕右下角
翻頁函數
駭客文化
- 用創意克服系統中的限制。
- 鼓勵創造,鼓勵動手修改程式的文化。
- 駭客的時間與精力,應該放在解決真正有趣的問題上。
- 無聊和乏味的工作是有罪的,所有人都不應該被愚蠢的重複性勞動困擾。
- 更多駭客文化請參看 Eric S Raymond 的 如何成為駭客 一文。
生活駭客
- 生活駭客不一定是享受 駭 的過程的純粹主義者, 但生活駭客知道如何把駭客技巧應用在生活中,過得更加舒服。
- 誰說軟體工程師不會是電腦白痴?
- 做個聰明的使用者,以程式取代重複、無意義的工作。 重複無意義的工作,是機器、程式的工作,不是人的工作。 (例如在二個試算表中,用人眼找出相同的類別,手動複製到第三個表中。)
- 什麼是駭客:自動化所有超過 90 秒的工作
- 使用電腦,而不要被電腦使用;使用程式,不要使用 工人智慧 。
伸手可及的距離
- 取用程式的門檻有多高?啟動編輯器、匯入、編寫程式、匯出,會比剪下貼上十次久嗎?
- 對多數 windows 使用者,確實很久;需要打開 ide。 (所以 linux user 都習慣平常就開著一個終端機。)
- 很幸運的,一些軟體有 內建 ide 。
軟體與他們內建的語言
- 要自動化 excel 或其它 ms office,你需要學 vba。
- 自動化 google 試算表或其它,要學 google app script。
- 學 javascript,你可以自動化所有網站。
開發者友好的瀏覽器
- 多數瀏覽器都內建了開發者工具。
- 來自良好的駭客傳統; 最早的瀏覽器同時也是編輯器,www 是互相的,鼓勵使用者建立自己的網頁。
開發者工具
瀏覽器 F12 開啟或右鍵檢查, 各種意義上都很好用。
快速選擇元素
使用開發者工具的 挑選頁面中的元素 , 把畫面上的元素在 檢測器 中的文件樹中顯示, 可以用來快速查看 html 文件結構。
在主控台中存取元素
在檢測器中的選取的元素,
可以在 主控台 中使用變數 $0
存取。
鎖右鍵圖片時
https://dq.yam.com/post.php?id=9229
document.querySelectorAll('*[oncontextmenu]').forEach(node => {
node.removeAttribute('oncontextmenu')
})
批次輸入多個輸入框
程式本體
let scoreList = scoreData.split(/\n/g)
document.querySelectorAll('input[type=text]').forEach((node, i) => {
node.value = scoreList[i]
})
處理 io
- 從剪貼簿貼上是最快的作法;
無論是在 excel、word 中框選後貼上,
或是用命令
xsel --input --clipboard < file.txt
。 - 使用 js 的反引號字串,可以在主控台內輸入含換行的資料。
- 使用
prompt()
可以簡單讀取使用者輸入,但換行會被換成空白。
多行字串的處理
簡單與使用者互動
- 善用
alert()
prompt()
confirm()
等活化石函數。 - 用
console.log()
或alert()
輸出結果。 - 如果結果較長,可以建立一個
<pre>
元素再將結果填入,body.appendChild(pre)
加到結尾。 - 也可以把結果輸出到剪貼簿,或把結果存到 blob 物件,
再用
URL.createObjectURL()
產生下載連結。
熟悉你的工具
如果你發現你常使用主控台做事,那找個時間好好認識一下開發者工具, 找篇文章或是 google、mdn 的文件來讀,會受益良多的。
厭倦不斷貼上重複的程式碼了嗎?
- 每次使用某個網頁時,都固定打開發者工具,寫出或貼上同樣的程式碼?
- 依照駭客文化,你應該要找出方式來簡化這個過程。
- 來看看 小書籤 吧。
- 或是你可以試試 firefox 內建的程式碼片斷編輯器。
小書籤
一種特殊的 url schema,
和 data:
一樣不指向某一位置,
而是本身帶有意義。
技術細節
javascript:
後的程式會在 global scope 下執行。- 如果有回傳值,回傳值會被轉成字串,被當成下一個頁面的 html 內容。
- 如果有多個 statement,和
eval()
一樣是用最後一個 statment 的回傳值。
回傳值
安裝小書籤
- 如同一般的 url,可以被 加入書籤 。
- 可以用拖拉的方式,或右鍵加入書籤;讓麻瓜也能使用。
- 因此可以 打包 一段程式碼成一個書籤, 方便使用與分享;不用再一直開開發者工具了。
- 點擊書籤後可以在當前頁面執行。
- 執行小書籤等於執行任意程式碼,所以不要執行來路不明的小書籤。 小書籤可以看到執行時所在網頁的所有內容, 包含 cookie,所以可以盜取帳號。
發布需知
- 因安全考量,一般的網站基本上不允許插入小書籤程式碼, 所以至少需要一個靜態網站,例如 github page 或是 jsfiddle 這類可以托管完整網頁的。 (或是叫使用者自己複製程式碼,然後在書籤列新增。)
- 程式碼太長書籤放的下嗎?我最多放過 5 KB 的程式。
- 真的太長怎麼辦? 打包成一個 js 檔,托管到靜態網站上, 在小書籤內容則是產生一個指向該檔案的 script 標籤,匯入執行。
範例
- jsfiddle 基本上可以托管任何 javascript, 就把小書籤編碼後,嵌入在 html 入放入就可以了。
- github page 是指托管靜態網站那種, 我自己把寫的小書籤都放在 bookmarklet repository , 雖然 github repository readme 裡的小書籤不會顯示, 但 靜態網頁 上是會顯示的。
youtube 搜尋小書籤
const keyWord = prompt('youtube 關鍵字')
window.open('http://youtube.com/search?q=' + keyWord)
小書籤 youtube 關鍵字搜尋 , 為什麼第二次點擊就不能執行了?
作用域
只呼叫函數 ,收藏後還可以執行嗎?
把用到的函數包進來
void function() {
function promptYoutube() {
const keyWord=prompt("youtube 關鍵字");
window.open("http://youtube.com/search?q="+keyWord)
}
promptYoutube()
}()
void 算符
undefined === void anyExpression
函數表達式
void function () {
// do what you want
)()
打包程式碼
- 可以將整個程式放在 immeditate invoke function 裡,防止撞名。
- 用 百分號編碼 去除特殊字元。
- 用 uglify-js 去除多餘空白與縮排,減少體積。
javascript url 的用途到底是什麼?
- 製造一個點擊後執行特定函數的元素,類似 onclick 的功能。
- 此外 javascript 的發明者 Brendan Eich, 特地將它設計成可以被加入書籤中,還幫他設計了 void 算符。
<a href="javascript:doAction('menu')">open menu</a>
<a href="javascript:doAction('save')">save change</a>
<a href="javascript:doAction('exit')">discard change</a>
相對 onclick
onclick 回傳 false 表示不處理本來應該處理的事件,
例如 <input type="submit">
如果 onclick 回傳 false 時不會提交,
<a href="//google.com"
如果 onclick 回傳 false 時不會前往新頁面。
data url
- data url 是以
data:
開頭的 url,他就是把資料直接放在網址。 - 例如圖片可以以 data url 內嵌在網頁裡。
<img src="data:image/png;base64,aGV5Cg==">
- data url 同樣可以加入書籤。
- 小書籤是把 js 打包成一書籤,而 data url 則是把整個網頁, 不只 javascript,連 html 一起打包進來。
- 例如這個 screen message 就是很有用的小工具。 我有把他整個網頁打包成一個 data url ,加到書籤, 方便離線時存取。
小書籤的侷限
- 必須手動打開書籤列點擊執行。
- 如果在某網頁固定用到該功能,還是要固定手動點擊才能執行。
- 有沒有辦法在進到某一網頁時,就執行對應的程式呢? 像是固定隱藏網頁中的某個側邊欄、改變字體顏色大小。
GreaseMonkey
- 暱稱油猴腳本,是管理
*.user.js
的擴充元件。 - firefox 有一些參數,可以在 about:config 裡調。
- 古早時期,有人會寫一份 javascript,
在啟動 firefox 時執行去調那些參數,
那份 js 習慣會取名為
*.user.js
。 - 後來有人在裡面作很多奇怪的事惡搞網頁,這些
*.user.js
量越來越多就需要一種管理的方法。 - 好像還有
*.user.css
,但我沒有研究。
功能
- 首先,需要安裝 grease monkey 這個擴充元件, 其它 user.js 腳本會由 grease monkey 擴充元件管理、執行。
- 提供編輯器和管理介面。
- 管理在每個網頁下要載入哪些 user.js。
- 在進入附檔名為
*.user.js
的 url 時,會自動詢問安裝。
安裝與使用
- //gist.github.com 裡按 raw,正常會出現原始碼, 有裝 grease monkey 會詢問安裝。 bbsjs gist
- 只要網址結尾是
.user.js
,就會跳出安裝介面, 就算托管的空間如 pastebin 不支援客制化網址,也可以在網址尾端加上?script.user.js
或#script.user.js
的方式觸發安裝。 - grease monkey wiki
pastebin 托管範例
pastebin 的好處是免註冊即可使用。
在 pastebin 裡 創建腳本後 ,
點擊 raw 直接取得原始碼 ,
並 修改網址結尾為 x.user.js
。
gist 托管範例
在 gist 創建腳本 ,檔名取為 *.user.js
,
點擊 raw 即可直接取得原始碼 ,
且網址結尾為自動成為對應的檔名。
metadata header
https://wiki.greasespot.net/Metadata_Block
// ==UserScript==
// @name chrome bbs javascript executor
// @namespace http://gholk.github.io/
// @description press J in term.ptt.cc can run JavaScript
// @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @match https://term.ptt.cc/*
// @match https://www.clam.ml/*
// @match https://www.ptt.cc/bbs/*
// @version 13
// @grant GM.notification
// @grant GM.xmlHttpRequest
// ==/UserScript==
執行環境
- window 是假的,真的在 unsafeWindow。
- 程式是被包在一個匿名函數裡執行,所以宣告的變數不會變全域變數。
- jquery 有時會炸,原因不明。
- 事件請愛用原生操作
*.addEventListener
,jquery 的會炸。 - 操作 dom 還沒什麼問題,可以用原網頁的 jquery:
const $ = unsafeWindow.$
。
GM.*
API
- GM.notification 可以發通知
- GM.xmlHttpRequest 不受 same origin policy 限制,超爽的
- GM.setClipboard 可以簡單複製內容
- api 都是 promise 非同步,不過除非你很重順序,不然直接呼叫時間也不會差太多
- https://wiki.greasespot.net/API
GM.notification('hello (1)')
.then(() => {
return GM.notification('hello (2)')
})
.then(() => console.log('after send 2 notify'))
功能鍵
- 以前有 api 可以在 grease monkey 的選單上設置按鈕。
- 不過 firefox 57 quantum 後,api 不允許了。
- 綁鍵盤
window.addEventListener('keydown')
。 - 改用 bookmarklet,本身就是要點擊才能執行的。
- grease monkey 較適合直接改 dom 而不是點擊執行。 像聽 hover 事件、在網頁上加按鈕。
擴充元件
- 功能更多,彈性更大。 可以做到上 bbs、修改歷史記錄、新增工具列按鈕等功能。
- 權限更清楚。在安裝時會清楚告訴你用到哪些功能。
- 小書籤雖然要點擊才會執行, 但除非看原始碼,不然完全不知道執行時做了什麼。
- 油猴腳本在安裝時會告知腳本會在哪些網頁下運作, 用到哪些 api,但給的還是很籠統。
- 擴充元件的安全性更有保障; 且 chrome 與 firefox 都會審覈擴充元件的程式碼,較安全。
擴充元件與油猴的安全性
因為擴充元件太複雜
雖然我寫過,但今天就不介紹了。
總結
- 開發者工具適合快速測試、編寫一次性腳本。
- 小書籤適合需要時才執行的功能,例如批次下載、自動砍站。
- 油猴腳本通常用來優化使用體驗,像放大影片、預覽圖片。 較進階的用法也可以在頁面中插入按鈕, 點擊時再執行類似小書籤的一次性功能。 例如一些 youtube 的油猴腳本就會在頁面中加一堆按鈕, 提供五花八門的功能。
什麼時候該用擴充元件?
- 當上面三者的都無法提供你需要的 api 時,例如 bbsfox、連 ftp。
- 當你需要寫出
讓麻瓜安心使用真正能用或能賣的東西 , 而不是客群只限定在 geek 的程式。 你要有一個正規的管道來發布,最好的選擇就是上架 chrome store 或 mozilla add on,讓使用者能放心安裝。
重用小書籤與 user.js 的程式碼
- 有時你會想要發布一個程式到二種平台, 小書籤的好處是所有人都能用,但 user.js 用起來比較方便。
- 可以透過
typeof GM
判斷是否在 grease monkey 下運行。
if (typeof GM == 'object') runInGreaseMonkey()
else runInBookmarklet()
許願池
你有什麼功能想做嗎? 或是想練手卻不知道做什麼嗎? 來許願吧!
簡單 grease monkey repl
Read Evaluate Print Loop, 可以輸入表達式,程式會調用 eval 然後印出求值結果。 如果輸入是 promise,會等 resolve 後才顯示結果。
FB 備份功能
把一份貼文備份成 JSON 或其它格式, 並在原文被刪除時可以重發, 在每層樓 tag 原本的留言者並附上當初的留言。
BBSJS
在 ptt 上執行 javascript, 類似當初 BBSLua 的概念, 但是跑在瀏覽器上而不是 server 上。
clean window
開啟一個沒有工具列、分頁列、網址列的乾淨視窗,
同義於 chromium --app=https://www.youtube.com/embed/dWKNW5lm3Ss
。
通常是用在看 youtube 時想空出多點空間。
http://youtu.be/dWKNW5lm3Ss 。