概要
皆さんは、ブックマークレットというものをご存知でしょうか。端的に説明すると「JavaScriptでページに様々な処理を掛けられるもの」なのですが、それによってページのHTMLを書き換えたり、別のURLへ遷移させたりするなどの便利な機能を手軽に利用することができます。
しかし、いざ自作しようとしても、どういう風に書けば良いのかがよく分からない人が多いのではないでしょうか?
そこで今回は、Twitter向けのとあるブックマークレットを題材にして、「どういう風に書けばどう動くのか」を分かりやすく説明できたら良いなと思います。
今回使用する題材
こちらのブックマークレットです。実は私自身が最初に参考にしたブックマークレットなんですけどね
ツイッターでアップロードした画像のオリジナル画像を表示するブックマークレット
解析する前に知っておきたい最低限の知識
前述した通り、ブックマークレットはJavaScriptで書かれています。なのでJavaScriptの知識はあればあるだけ望ましいですが、他にも注意すべきポイントがあります。
ブックマークレットはブラウザのブックマークのURLに入力して保存しておくものです。なので、ブックマークレットには文字数制限があります。外部のJavaScriptを読み込んで回避することは可能ですが、そこまでしなくてもコンプレッサー(例1、例2)を使って文字数を削減することができます。
また逆に、他人のブックマークレットを解析したい場合は整形ツール(例1、例2)を使えばグッと読みやすくなります。幸い、上記で例に出したTwitterブックマークレットは整形されたソースも載っているので手間が省けるんですけどね。
ポイントその1:特定のメソッド・プロパティに注目する
ブックマークレットで実行されるスクリプトからは、当該Webページにおける様々な情報を取得できます。よく使われるものとしては、次のようなものが挙げられるでしょうか。
- javascript : (function (){~})();……ブックマークレットの定番フレーズ(≒おまじない)です。要するに、余計な部分に影響を与えないように
- document.location.href……ブックマークレットを使用したWebページのURLを表します。
- document.getElementsByTagName("tag")……特定のタグで囲われた部分のオブジェクトを取得します。「Elements」から分かるように配列として取得可能です。更に1要素深いタグで囲われた部分を取得したい場合は、そのままgetElementsByTagNameを重ねればいいです。なお、HTMLの文字列が欲しい場合はinnerHTMLプロパティで参照しましょう。
- window.open()……新しいウィンドウを開きます。()内にURLを指定すればそのページが開きますし、window.open()自体をオブジェクトとして持っておき、document.write()メソッドで別に用意したHTMLを開かせることも可能です。
- console.log()……コンソールにログを表示します。コンソールというのは、大雑把に言えばブラウザでF12を押せば出てくるアレのことです。ChromeやFirefoxどころかIEですら普通に出てくるので、使わない方が損なのがコンソールなのですが、そこでデバッグする際に役立つのがこのメソッド。さり気なくprintfみたいに使えるので非常に便利です。
ポイント2:繰り返し処理をする際の定番表現
先のTwitterブックマークレットでは、indexOfメソッドやsplitメソッドを効果的に活用することにより、画像のURLを特定して別ウィンドウに表示させています。このコードで重要なのが、posAとposBという2変数を利用することで、URLの候補となりえる部分を効率的に切り出しているということです。
HTMLの中には、同じような記述が繰り返し並んでいることがよくあります。ですので、「文字列Aと文字列Bに挟まれた文字列を切り出したい」といった需要は大きいのです。
製作例
ではここで、とある画像投稿サイト用のブックマークレットを組み上げてみるとしましょう。そのサイトの場合、「投稿URLのアッサリさに反して画像URLがそこそこ長め(倍以上違う)」という性質がありますので解析しがいがありそうです。
ただ、画像URLが「何とか/数字列1/数字列2/数字列3」なのに、投稿URLのページにあるソースでは数字列3しか分からないので苦戦しました。実はリンク先URLからリダイレクトされた先のURLに数字列1と数字列2があるといった仕様(しかも数字列1と数字列2はリダイレクトする度に変化する)なので、一旦location.hrefを書き換えてから再び取得し直すといった作業が必要になります。
とは言え、そのままの発想で適当に組んで動かしてみると、リダイレクトする前に先の処理へ突っ走ってしまうので全然うまく動きませんでしたorz
javascript : (function (){
var url = location.href;
var pos_id = url.indexOf('im');
var id = url.substring(pos_id + 2, url.length);
location.href = 'http://seiga.nicovideo.jp/image/source/' + id;
var image_url = location.href;
image_url = image_url.replace('/o/', '/priv/');
location.href = image_url;
})();
で、適宜waitを入れようとしたわけですが、setTimeoutをsetIntervalを使っても上手く行かないんだよ!ビジーウェイトだとそもそも画面の更新を妨害するし俺は一体どうしたらいいんだ!誰か教えて下さい何でもしますから!
というわけなので方針を転換して、関連静画を別ウィンドウで開くスクリプトを作成しました。
// 展開時
javascript : (function (){
var str = document.getElementsByTagName('body')[0].innerHTML;
var posX = str.indexOf('他のイラスト<');
var posY = str.indexOf('>もっと見る<');
var posA = str.indexOf('/seiga/', posX);
var posB = str.indexOf('\"', posA + 7);
while ((posA !=- 1) && (posA < posY)){
url = 'http://seiga.nicovideo.jp/seiga/' + str.substring(posA + 7, posB);
window.open(url);
posA = str.indexOf('/seiga/', posB + 1);
posB = str.indexOf('\"', posA + 7);
}
})();
// 圧縮時
javascript:(function(){var str=document.getElementsByTagName('body')[0].innerHTML;var posX=str.indexOf('他のイラスト<');var posY=str.indexOf('>もっと見る<');var posA=str.indexOf('/seiga/',posX);var posB=str.indexOf('\"',posA+7);while((posA!=-1)&&(posA<posY)){url='http://seiga.nicovideo.jp/seiga/'+str.substring(posA+7,posB);window.open(url);posA=str.indexOf('/seiga/',posB+1);posB=str.indexOf('\"',posA+7)}})();
また、他の画像投稿サイト用のブックマークレットを1つ公開しています。
https://twitter.com/YSRKEN/status/643635285012541440