Professional Documents
Culture Documents
⼀、⽤途
增加瀏覽網⾴時的額外功能
推送額外的訊息給使⽤者
改變網⾴的內容
網⾴機器⼈
其他
延伸閱讀
http://tonytonyjan.net/2012/05/25/get-start-with-chrome-extension/
⼆、參考案例
朕知道了
https://chrome.google.com/webstore/detail/%E6%9C%95%E7%9F%A5%E9%
81%93%E4%BA%86/igoniplmkmcggoogbmkmdmnmkablnmnm?hl=zh-TW
inLiveTW
https://chrome.google.com/webstore/detail/inlivetw/ hcffinobmpdchcoapdeoin
hdmlihiok
柯⽂哲關鍵字⼩幫⼿
https://chrome.google.com/webstore/detail/%E6%9F%AF%E6%96%87%E5%
93%B2%E9%97%9C%E9%8D%B5%E5%AD%97%E5%B0%8F%E5%B9%AB
%E6%89%8B/moldcnjkjmceelkphffaeoodjknbn hn
三、環境基礎
需先安裝 Chrome 瀏覽器
Chrome extension 是以⼀般的網⾴程式語⾔:HTML、CSS、Javascript 即可撰寫,
設定檔部分為 JSON 格式
四、範例實作 (⼀)
範例網址
https://github.com/goooooooogle/chrome-extension-example/tree/master/hello
範例題⽬
建⽴⼀個最基礎的 Chrome extension,並了解其運作架構與撰寫⽅式。
在本範例中,將撰寫⼀個 extension 的下拉 popup,並在其中顯⽰「Hello World!」⽂字
參考教學網址
. https://developer.chrome.com/extensions/getstarted
. http://blog.longwin.com.tw/2014/01/chrome-extensions-plugin-develop-2014/
本範例使以上述網址提供之檔案架構改寫⽽成。原案例是以取得 之 ,取得最新之 flickr api
照⽚,並提供給使⽤者,由於有 之問題,故在本範例中將此部份去除,僅留下最基
api key
本的 popup呈現
基礎架構
請先在任意處創建⼀個放置 extension files 的資料夾,並在其中創建如下所⽰之檔案(或
是直接從上⽅參考教學網址中下載範例檔案),在本範例中將資料夾名稱命名為
「hello」:
/hello
﹂ icon.png // 套件的 icon
﹂ manifest.json // 控制套件的相關參數都寫在這裡
﹂ popup.html // popup 的內容
開始撰寫
依照上述架構完成後,請將各檔案內容如下⽅所述撰寫:
1. 套件 Icon的部份可以設置為⾃⼰喜歡的任意圖⽚,尺⼨為 19x19px,這個圖⽚會顯⽰在
套件列上,如下所⽰:
2. manifest.json 內容設置如下:
{
"manifest_version": 2,
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
}
}
關於 manifest 的部份想了解更多請參考此網址
3. popup.html ,如同⼀般 HTML 的撰寫,在此將內容設置如下:
<!doctype html>
<html>
<head>
<title>Chrome Extension Example</title>
<style>
body {
width: 200px;
overflow-x: hidden;
}
</style>
</head>
<body>
<h2>Hello World!</h2>
<p>This is my first Chrome extension</p>
</body>
</html>
. 完成後會出現在下⽅擴充功能清單裡
. 恭喜你完成了⼈⽣第⼀⽀ Chrome extension 的撰寫
五、範例實作 (⼆)
範例網址
https://github.com/goooooooogle/chrome-extension-example/tree/master/unfold-
links
範例題⽬
建⽴⼀個 Chrome extension,於 popup 中顯⽰常⽤⼯具的連結清單,並使⽤外部的
stylesheet 樣式
基礎架構
本範例之基礎架構與範例(⼀)相同。在本範例中將資料夾名稱命名為「unfold-links」:
/unfold-links
﹂ icon.png
﹂ manifest.json
﹂ popup.html
開始撰寫
依照上述架構完成後,請將各檔案內容如下⽅所述撰寫:
. 套件 Icon的部份與範例(⼀)相同
. manifest.json 內容設置如下:
{
"manifest_version": 2,
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"http://*maxcdn.bootstrapcdn.com*", "webRequest"
]
}
在這個範例中,我們將使⽤外連的 bootstrap stylesheet,以加快開發速度,因此需另
外設定外部連結的存取權限
. popup.html ,在此將內容設置如下:
<!doctype html>
<html class="no-js" lang="zh-TW">
<head>
<meta charset="utf-8">
<title>unfold Tools</title>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.co
m/bootstrap/3.3.1/css/bootstrap.min.css">
<style>
body {
width: 200px;
overflow-x: hidden;
}
a:focus {
outline: none;
}
.list-group {
margin-bottom: 0;
}
</style>
</head>
<body>
<div class="list-group">
<a href="https://app.asana.com/" class="list-group-item"
target="_blank">
Asana
</a>
<a href="https://hackpad.com/" class="list-group-item" t
arget="_blank">
Hackpad
</a>
<a href="http://www.qdm.ks.edu.tw/computer/webpage/" cla
ss="list-group-item" target="_blank">
首頁製作百寶箱
</a>
</div>
</body>
</html>
⾄此完成整個 Chrome extension 的撰寫
安裝套件
請依照範例(⼀)的⽅式安裝套件,完成後成果如下所⽰:
點擊清單連結後,會⾃動開啟新分⾴,並連結⾄該網站。
六、範例實作 (三)
範例網址
參考範例
https://github.com/lunastorm/ivereadthat
範例題⽬
建⽴⼀個 Chrome extension,在臉書發⽂框加⼊⼀個按鈕,點擊之後會⾃動在輸⼊框內容
最前⾯加⼊柯P的⼝頭禪
基礎架構
在本範例中將資料夾名稱命名為「facebook-kp-mantra-button」:
/facebook-kp-mantra-button
﹂ jquery.min.js
﹂ kpmantra.js
﹂ manifest.json
開始撰寫
這⽀ Chrome extension,直接以參考範例來作重作
. 研究臉書輸⼊框架構,歸納如下
a. 主要輸⼊框
b. 留⾔輸⼊框
. manifest.json 內容設置如下:
{
"manifest_version": 2,
"name": "不過我覺得是這樣啦吼",
"description": "讓你快速在臉書上引用柯P口頭禪",
"version": "1.0.5566",
"permissions": [
"https://www.facebook.com/"
],
"content_scripts": [
{
"matches": ["https://www.facebook.com/*"],
"js": ["jquery.min.js"],
"run_at": "document_end"
},
{
"matches": ["https://www.facebook.com/*"],
"js": ["kpmantra.js"],
"run_at": "document_end"
}
]
}
. kpmantra.js,在此將內容設置如下:
MutationObserver = window.MutationObserver || window.WebKitMut
ationObserver
var observer = new MutationObserver(function(mutations, observ
er) {
//在主發文框嵌入柯P按鈕
if($("._52lb div .kpmantra").length == 0) {
$("._52lb div:first").prepend('<div class="lfloat kpmantr
a"><a data-hover="tooltip" class="_1dsq _1dsv" aria-label="柯
P 口吻" title="柯 P 口吻" id="u_jsonp_2_a"><span class="_1dsr" s
tyle="background-image: url(http://graph.facebook.com/http://g
raph.facebook.com/'.$value['id'].'/picture?type=square&height=
8&width=8/picture?type=square);background-position:center;"><s
pan class="_4-px ellipsis">柯 P 口吻</span></span></a></div>');
}
//在回覆區塊嵌入柯P按鈕
$(".UFICommentAttachmentButtons").each(function() {
if($(this).find('.kpmantraComment').length == 0) {
$(this).prepend('<div aria-label="柯 P 口吻" class="UFICo
mmentStickerButton kpmantraComment" data-hover="tooltip" data-
tooltip-alignh="center"><div class="UFICommentStickerIcon" sty
le="background-image: url(http://graph.facebook.com/http://gra
ph.facebook.com/'.$value['id'].'/picture?type=square&height=8&
width=8/picture?type=square);background-position:center;backgr
ound-size:14px"></div></div>');
}
});
})
//主發文區塊的柯P按鈕的觸發event
$('body').delegate('.kpmantra','click',function(){
var content = $('input[name="xhpc_message"]').val();
var keydown = new Event('keydown');
document.querySelector('[name=xhpc_message_text]').dispatchE
vent(keydown);
var messageBox = document.querySelector('[name=xhpc_message_
text]');
messageBox.dispatchEvent(new Event('focus'));
messageBox.value = '不過我倒覺得是這樣啦吼,'+content;
messageBox.dispatchEvent(new Event('keydown'));
$('input[name="xhpc_message"]').val('不過我倒覺得是這樣啦吼,'+co
ntent);
$('textarea[name="xhpc_message_text"]').focus();
});
//回復區塊的柯P按鈕的觸發event
$('body').delegate('.kpmantraComment','click',function(){
var $editableDiv = $(this).parent().parent().find('.UFIAddCo
mmentInput').find('._54-z'),
$element = $(this).parent().parent().find('.UFIAddCommen
tInput').find('span span:first'),
$elementInput = $(this).parent().parent().find("input[na
me=add_comment_text]"),
content = $element.text();
$elementInput.trigger("keypress");
$editableDiv.trigger("keypress");
$editableDiv.focus();
if($element.length == 0) {
var span = $(this).parent().parent().find('.UFIAddCommentI
nput').find('span'),
placeholder = $(this).parent().parent().find('.UFIAddC
ommentInput').find('._5ywb'),
reactid = span.data('reactid');
placeholder.addClass('_5ywc');
span.html('<span data-reactid="'+reactid+'">不過我倒覺得是這
樣啦吼,</span>');
}
else {
$element.text('不過我倒覺得是這樣啦吼,'+content);
}
});
observer.observe(document, {
subtree: true,
attributes: true
})
實際效果
點擊後
七、範例實作 (四)
範例網址
參考範例
https://github.com/goooooooogle/chrome-extension-example/tree/master/ b-
kxmantra
https://github.com/kp-is-everywhere/kp-is-everywhere.github.io
https://github.com/jsoma/tabletop
範例題⽬
建⽴⼀⽀ Chrome extension,⾃動於 Facebook ⾴⾯的發⽂區塊中,加⼊⼀個下拉字句清
單,字句清單擷取字遠端google db,點擊項⽬後會⾃動加⼊發⽂⽂字內容
基礎架構
在本範例中將資料夾名稱命名為「 b-kxmantra」:
/fb-kxmantra
﹂ manifest.json
﹂ /css
﹂ style.css
﹂ /icons
﹂ icon16.png
﹂ icon48.png
﹂ icon128.png
﹂ icon256.png
﹂ /js
﹂ jquery-2.1.1.min.js
﹂ jquery-2.1.1.min.map
﹂ tabletop.js
﹂ kxmantra.js
﹂ inject.js
開始撰寫
這⽀ Chrome extension,以 javascript class + jquery plugin 的模式撰寫,並借助以
tabletop.js 這⽀ plugin 來快速簡單的取得 Google Spreadsheets 的資料
. ⾸先要申請Google Spreadsheets作為資料庫使⽤,步驟如下:
a. 先到Google⾴⾯,新增⼀個試算表⽂件
b. 將資料填⼊,在這裡要注意在第⼀⾏的部份,之後將作為欄位名稱使⽤(可使⽤中
⽂),⽬前的案例中總共有五欄資料
c. 接下來,請按「檔案」=>「發布到網路」
d. 藍⾊那顆按鈕給他⼤⼒的點下去
e. 然後我們會得到⼀個網址,這個網址就是之後串街資料庫時使⽤的網址,⾄此資料庫
設定完成。這邊得到的網址如下:
https://docs.google.com/spreadsheets/d/1TYidWSSP_iREXeCfSI745Xc6Q_o
Qk9tgrVFubLhOL14/pubhtml
$.fn.kxmantra = function() {
var mantra = new mantraGenerator();
mantra.generate();
return this;
};
}).call(this);
.完成
實際效果
⼋、範例(四)的延伸與更新
在接下來的部分,我們會以範例四作為基礎,作延伸的更新,更新項⽬如下:
讓「回覆」、「聊天室」也能使⽤
⽀援多個「語錄庫」之間的切換,讓「語錄庫」可以無限擴充
精簡程式碼
困難點與解決⽅法
. 上週有討論到關於因為臉書在「回覆區塊」,是使⽤reactjs,所以無法如同主要發⽂區
塊的⽅式達成輸⼊框區域內容的改動。
因為⽬前尚不熟悉reactjs,甚或了解之後可能也無法輕易操控臉書本⾝的code,所以在
這邊我以折衷的⽅式來達到相同的⽬的,即是讓使⽤者在點擊清單中的項⽬,幫使⽤者
將項⽬內容複製到clipboard,然後提⽰使⽤者⾃⾏貼上
. 「聊天室」的部份,雖然沒有回覆區塊這麼複雜,但是因為雖然可以更改<textarea>的
內容,也可以順利submit,但是無法取得使⽤者實際⾃⾏輸⼊的內容,有使⽤者經驗不
良的疑慮(無法取得實際內容=>無法在輸⼊點插⼊內容,與主輸⼊框之間的體驗不
同),因此在這邊,我採取與「回覆區塊」相同的⽅式實作,讓使⽤者⾃⾏貼上內容
套件架構
架構與原範例四基本上⼀樣
/fb-kxmantra
﹂ manifest.json
﹂ /css
﹂ style.css
﹂ /images
﹂ kx.png
﹂ /icons
﹂ icon16.png
﹂ icon48.png
﹂ icon128.png
﹂ icon256.png
﹂ /js
﹂ jquery-2.1.1.min.js
﹂ jquery-2.1.1.min.map
﹂ tabletop.js
﹂ kxmantra.js
﹂ inject.js
更新內容
. 在 manifest.json 我們將 extension 設置如下
更改的重點包含:
a. permissions,新增兩個 copy 動作所需的權限
b. 將 button 的圖⽚改為提取本機端的圖⽚,因此需設定web_accessible_resources
{
"name": "朕語錄",
"version": "0.0.6",
"manifest_version": 2,
"description": "讓你快速在臉書發文時引用皇上的語錄",
"permissions": [
"https://www.facebook.com/",
"clipboardRead",
"clipboardWrite"
],
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},
"web_accessible_resources": ["images/kx.png"],
"content_scripts": [
{
"matches": [
"*://*.facebook.com/*",
"*://*.google.com/*"
],
"css": [
"css/style.css"
],
"js": [
"js/jquery-2.1.1.min.js",
"js/tabletop.js",
"js/kxmantra.js",
"js/inject.js"
],
"run_at": "document_end"
}
]
}
. kxmantra.js 做了⼤幅度的調整與程式碼優化步驟,
因為篇幅過⻑,內容部分請直接造訪 Github ,更新重點如下:
a. DEBUG 模式:由於在過程中錯誤不斷,在這邊我參考了⾹蕉王的神code,撰寫了
DEBUG 模式,當參數設定為 true 時,會⾃動吐出程序的執⾏狀況
b. 在 mantraGenerator prototype 的部份,做了調整與優化:將generator與
getSentense結合,並將generator調整為「專注在取得資料庫與產⽣相應的html
code」,並將重複使⽤的 function 獨⽴出來
c. 新增⼀個「install」的 prototype,專⾨處理將 html 塞到⾴⾯中的動作
d. 將 bind 與 MutationObserver (這東⻄我第⼀次碰到,還不太懂)分開,bind 主
要處理使⽤者觸發的 event 內容;observer 則為當偵測到⾴⾯弄容改變時,作相應
的動作
e. 將重複⽤到的參數做簡化與統整:即 _el0,_ela,_el1,_el2,_el3 ... 這部份,讓程式碼更
精簡(會降低閱讀性,但是因為 facebook 的 class name 基本上也沒什麼閱讀性)
並提升⼀點執⾏效率
f. 在變數名稱上作規劃:例如「__」開頭的變數代表 function;「_html」開頭的代表
⼀個 html ⽚段;「_el」開頭代表⼀個精簡⽤的變數項⽬,el代表 element。
g. 選單項⽬操作時,新增「鍵盤動作」,讓使⽤者也可以⽤「上」、「下」、
「enter」來作選取的動作,請參考下⽅程式碼:
this.body.on('keydown', '.kxmantraList .kxsentence,.kxmant
raCommentList .kxsentence,.kxmantraChatList .kxsentence', func
tion(event) {
code = event.keyCode || event.which;
if(code == 13) {
event.preventDefault();
$(this).trigger('click');
}
else if(code == 40) {
var ul = $(this).parent();
ul.find('li').removeClass('active');
$(this).nextAll(_eld).first().addClass('active');
$(this).nextAll(_eld).first().find(_elh).focus();
}
else if(code == 38) {
var ul = $(this).parent();
ul.find('li').removeClass('active');
$(this).prevAll(_eld).first().addClass('active');
$(this).prevAll(_eld).first().find(_elh).focus();
}
});
h. 項⽬搜尋功能,有現成的js套件可以使⽤,但是這邊我以最簡單的 jquery 達成,請
參考下⽅程式碼:
this.body.on('click', _elb, function(event) {
event.stopPropagation();
});
this.body.on('change blur keyup', _elb, function() {
var filter = $(this).val(),
list = $(this).parent().find('ul:visible');
if (filter) {
list.find('span:not(:contains(' + filter + '))').par
ent().hide();
list.find('span:contains(' + filter + ')').parent().
show();
if(list.find('span:contains(' + filter + ')').length
== 0)
list.find(_elf).show();
else
list.find(_elf).hide();
} else {
list.find(_elg).show();
list.find(_elf).hide();
}
});
. 在 style.css 的部份,需注意的部份為「聊天室區塊」的樣式調整,因為「主發⽂區
塊」、「回覆區塊」皆為下拉的⽅式,但是聊天室位於⾴⾯最底端,無法下拉,所以必
須相對的調整 stylesheets
實際效果
. 主發⽂區塊
. 切換語錄庫
. 切換成功
. 回覆區塊的效果
. 回覆區塊提⽰使⽤者貼上
. 聊天室區塊,搜尋框在下⽅
已知BUG
. ⼀般狀況輸⼊⽂字時會變鈍鈍的,因為程式偵測網⾴變化次數過度頻繁
. 部分情況下,按鈕嵌⼊會失效,但是刷新後⼜正常