ユーザが閲覧しているWebページに対してなんらかのアクションをとらせたい場合、ブックマークレットの提供が選択肢になります。たとえば、Catchboxでもブックマークレットを提供しています。
ブックマークレットは、JavaScriptのコードをブックマークバーに追加することで実装しますが、実現したい処理自体をブックマークとして登録させてしまうと、アップデートを行うことができなくなります。
そこで、サーバサイドに処理内容を記述したJavaScriptファイルを配置し、そのJavaScriptファイルを、閲覧しているWebページのbody部の最後に追加する処理を、ブックマークレットとしてユーザに登録させます。
これは、ユーザがブックマークバーに追加する処理です。(分かりやすいよう改行とコメントを加えていますが、実際には1行に圧縮して提供します)
javascript:(function(){
// サーバに配置したJSのドメイン
// 変数名は、元のページのJavaScriptの変数と競合しないようにプレフィックスをつけます
BML_D='playcatchbox.com';
// サーバに配置したJSを読み込むためのscriptタグ
BML_SCRIPT=document.createElement('script');
BML_SCRIPT.type='text/javascript';
BML_SCRIPT.src='//'+BML_D+'/b/b.js';
// body部の最後にscriptタグを追加
document.getElementsByTagName('body')[0].appendChild(BML_SCRIPT)
})();
これで、閲覧中のページにサーバ側に配置したJavaScriptを読み込ませる処理ができました。
次に、サーバ側に配置したJavaScriptです。
以下のようなフォルダ構成でファイルを用意します。
htdocs
・ b < JavaScriptを格納するフォルダ >
・ b.js <処理内容が記述されたJavaScriptファイル>
・ lib.all.js
・ lib.js
lib.all.jsには jQueryの最新版 と jQuery postMessage の両方を記述します。
lib.jsには jQuery postMessageのみを記述します。
その際、jQuery postMessageに、jQuery 1.9系以降で廃止になった $.browser 関数が使われているため、これを置き換えます。
// has_postMessage = window[postMessage] && !$.browser.opera;
has_postMessage = window[postMessage] && !window.opera;
ブックマークレットの処理内容が書かれた r.js を見ていきます。
(function() {
// JQueryの最低バージョン
var v = "1.3.2";
if (window.jQuery === undefined || window.jQuery.fn.jquery < v) {
// jQueryが元のページで使用されていなかったら読み込んで処理を実行
BML_LOAD_LIB('lib.all.js', function() {
BML_BOOKMARKLET();
});
} else if (false === jQuery.isFunction(jQuery.receiveMessage)) {
// jQuery postMessageが元のページで使用されていなかったら読み込んで処理を実行
BML_LOAD_LIB('lib.js', function() {
BML_BOOKMARKLET();
});
} else {
// ライブラリがすべて元のページで読み込まれていたら処理を実行
BML_BOOKMARKLET();
}
function BML_LOAD_LIB(BML_LIB_FILENAME, BML_CALLBACK) {
var BML_IS_DONE = false;
// ライブラリの読み込み
var BML_LIB = document.createElement("script");
BML_LIB.src = '//' + BML_D + '/b/' + BML_LIB_FILENAME;
BML_LIB.onload = BML_LIB.onreadystatechange = function() {
if (!BML_IS_DONE && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
// ライブラリが正しく読み込まれたら処理を実行
BML_IS_DONE = true;
BML_CALLBACK();
}
};
document.getElementsByTagName("head")[0].appendChild(BML_LIB);
}
function BML_BOOKMARKLET() {
(window.myBookmarklet = function($) {
if ($("iframe#bookmarklet-overlay").length > 0) {
return;
}
// 読み込むiframeタグを生成
var $BML_IFRAME = $('<iframe/>');
$BML_IFRAME.attr({
'allowtransparency' : 'true',
'scrolling' : 'no',
'id' : 'bookmarklet-overlay',
'name' : 'bookmarklet-overlay',
'src' : '//' + BML_D + '/bookmarklet/' + encodeURIComponent(location.href)
}).css({
'border' : 'none',
'height' : '100%',
'width' : '100%',
'position' : 'fixed',
'z-index' : 2147483647,
'top' : 0,
'left' : 0,
'display' : 'block'
});
$('body').append($BML_IFRAME);
// iframe側からpostMessageで'close'のメッセージを受け取ったら、追加したiframeを削除
$.receiveMessage(function(e) {
if (e.data === 'close') {
$BML_IFRAME.remove();
}
});
})(jQuery);
}
})();
最後に、iframe内に表示されるページ (上記の例であれば http:// [ドメイン] /bookmarklet/ [読み込み元のURL] )から、自身を削除するメッセージを送信するための記述を見ていきます。
これは上記のサンプルコードが、元のページに覆いかぶさるようにiframeが配置するため、そのiframeが表示されている間は、その下に隠れる元のページを操作することができなくなります。
iframe内に表示されたページで処理を終えた後は、上に覆いかぶさったiframeを取り除いてあげる必要がありますが、iframe内に表示されるページと、iframeが追加されたページ(元のページ)はドメインが異なるため、このままでは、iframeを削除するための処理を、iframe内に表示されたページ側から行うことはできません。(クロスドメイン問題)
そこで、jQuery postMessage pluginを使用して、iframe内に表示させるページから、元のページへmessageを送り、そのmessageを受け取った元のページ側に読み込まれたJSファイルにより、iframeを除去します。
<script type="text/javascript">
function close {
// 'close'のメッセージを送信
$.postMessage('close', 'http:// [元のページのURL]', parent);
}
</script>
(現時点の課題)
* このブックマークレットは、外部JSの読み込みを許可していないサイト(Facebookなど)では作動しないです。