ブックマークレット実行時に外部ファイルをロードして使う
基本
- 前回の外部ファイルをロードして実行するブックマークレットは、こんな感じだった。
javascript: (function(d,s){ s=d.createElement('script');s.src='//dl.dropboxusercontent.com/u/XXXXXXX/bookmarklet.js';d.body.appendChild(s); })(document)
- Webページのbodyタグに以下のようなscriptタグを追加して、
- シンプルに、たった一つの外部ファイルをロードしているのだ。
<script src='//dl.dropboxusercontent.com/u/XXXXXXX/bookmarklet.js'></script>
複数の外部ファイルをロードする
- では、外部ファイルを二つ以上ロードしたいときはどうするべきか?
- 方法としては、ロードしたい外部ファイルの数だけscriptタグを追加すればいいはず。
- 外部ファイル二つなら、コピペして、srcのURLだけ変更すればいいのかもしれない。
javascript: (function(d,s){ s=d.createElement('script');s.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js';d.body.appendChild(s); s=d.createElement('script');s.src='//dl.dropboxusercontent.com/u/XXXXXXX/bookmarklet.js';d.body.appendChild(s); })(document)
- では、外部ファイルを三つ以上ロードしたい時も、コピペを繰り返すべきなのか?
- いや、やめておこう。URLだけ指定すればロードしてくれる、そんな関数が欲しい。
javascript: (function(d,urls,i,s){ for(i=0;i<urls.length;i++){s=d.createElement('script');s.src=urls[i];d.body.appendChild(s)}; })(document,['//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js','//dl.dropboxusercontent.com/u/XXXXXXX/bookmarklet.js'])
これで外部ファイルがいくつ増えても、URLを追加するだけでロードしてくれる!
外部ファイルをロードしてからブックマークレット内部のコードを実行する
- 上記までのように、すべてのコードを外部ファイルにしてしまうのも一つの方法である。
- でも通常は、ライブラリのみ外部からロードして、目的のコードはブックマークレット内部に書いておきたい。
はじめの一歩
- そして、最初に思いつくのはこんなコード。(alert部分がブックマークレット内部のコード)
javascript: (function(d,s){ s=d.createElement('script');s.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js';d.body.appendChild(s); /* 内部コード */ alert(typeof CryptoJS); alert(CryptoJS.AES.encrypt('hello','1234')); })(document)
- しかし、上記のコードを実行してみると、正常には動かない...。
- 事前準備として、どこかのページを新規に開いておく。
- キャッシュを空にして、ページを再読み込みしておく。
- 気を取り直して、もう一度、同じページで実行するとちゃんと動く。
- 最初に動かなかったのは、気のせいだった訳ではない。
- CryptoJSが、しっかり"undefined"になっているのだ。
- おそらく、crypto-jsライブラリのロードが完了する前に、次のalert文の実行が始まってしまったのだ。
setTimeoutを使う
- 外部ファイルがロードされるタイミングと、内部コードが実行されるタイミングに気を付けて書き直し。
javascript: (function(d,s){ s=d.createElement('script');s.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js';d.body.appendChild(s); setTimeout(function(){ /* 内部コード */ alert(typeof CryptoJS); alert(CryptoJS.AES.encrypt('hello','1234')); },1000); })(document)
- キャッシュを空にして、ページを再読み込みして、事前準備完了。
今度は、初回から正常に動作した!
- setTimeoutで、内部コードの実行を1秒遅らせてみたのだ。
- その間に、crypto-jsのロードが完了して、CryptoJSオブジェクトが利用可能になるのだ。
onloadを使う
- しかし、必ず1秒待機するのは無駄が多い。
- あるいは、1秒待機してもロードは未完かもしれない。
- 本来は、ロードが完了したタイミングで、すぐに内部コードを実行するのがベスト。
- onloadイベントハンドラを使うと、ロードが完了ししたタイミングで実行できる。
javascript: (function(d,f,s){ s=d.createElement('script'); s.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js'; s.onload=function(){f()};/* <---onload属性を追加 */ d.body.appendChild(s); f=function(){ /* 内部コード */ alert(typeof CryptoJS); alert(CryptoJS.AES.encrypt('hello','1234')); }; })(document)
これで、無駄に待機せずに素早く実行できるようになった!
onloadするのは最後だけ
- 複数のライブラリを読み込む時は、最後のロードだけonloadする。
- 毎回onloadすると、内部コードの実行が繰り返えされてしまう...。
javascript: (function(d,f,s){ s=d.createElement('script'); s.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha256.js'; d.body.appendChild(s); s=d.createElement('script'); s.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js'; s.onload=function(){f()};/* <---onload属性を追加はここだけ */ d.body.appendChild(s); f=function(){ /* 内部コード */ alert(CryptoJS.SHA256('1234')); alert(CryptoJS.AES.encrypt('hello','1234')); }; })(document)
for+onload
- 複数の外部ファイルをロードするforを使った技と組み合わせてみた。
javascript: (function(urls,i,s,f){ for(i=0;i<urls.length;i++){ s=document.createElement("script"); s.src=urls[i]; if(i==urls.length-1){s.onload=function(){f()}}; document.body.appendChild(s); }; f=function(){ /* 内部コード */ alert(CryptoJS.SHA256('1234')); alert(CryptoJS.AES.encrypt('hello','1234')); }; })(["//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha256.js","//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"])
内部コードの関数を引数渡し
- 外部ファイルのロードと内部コードを書く部分が明確に分かれる所が好き。
javascript: (function(f,urls,i,s){ urls=["//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha256.js","//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"]; for(i=0;i<urls.length;i++){ s=document.createElement("script"); s.src=urls[i]; if(i==urls.length-1){s.onload=function(){f()}}; document.body.appendChild(s); }; })(function(){ /* 内部コード */ alert(CryptoJS.SHA256('1234')); alert(CryptoJS.AES.encrypt('hello','1234')); })
jQueryを使う
基本
javascript: (function(f,urls,i,s){ urls=["//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"]; for(i=0;i<urls.length;i++){ s= document.createElement("script"); s.src=urls[i]; if(i==urls.length-1){s.onload=function(){f()}}; document.body.appendChild(s); }; })(function(){ /* 内部コード */ alert($().jquery); })
コンフリクト防止
- jQueryはよく使われるライブラリである。
- 様々なバージョンがあって、中には互換性のないバージョンもあったりする。
- また、jQueryやprototypeなどはシンプルに書くため、$変数にそのオブジェクトを代入する。
- jQueryやprototypeなどを使っているページで上記ブックマークレットを実行するとどうなるか?
- 困ったことに、現在のページが正常に動作しなくなってしまう恐れがあるのだ...。
- ブックマークレットがロードしたjQueryによって、$変数が書き換えられてしまうため。
- そんなコンフリクト(変数名の衝突)を避けるために、jQueryは一つの方法を用意している。
jQuery.noConflict();
var myQuery=jQuery.noConflict(true);
- jQuery.noConflict(true)を使って、書き直してみる。
javascript: (function(f,urls,i,s){ urls=["//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"]; for(i=0;i<urls.length;i++){ s= document.createElement("script"); s.src=urls[i]; if(i==urls.length-1){s.onload=function(){f(jQuery.noConflict(true))}}; document.body.appendChild(s); }; })(function($){ /* 内部コード */ alert($().jquery); })
すぐに使えるワンライナーの雛形
-
-
- URL = ロードする外部ファイルのURL。
- 内部コード = ブックマークレット内部に書くコード。(ロードしたライブラリを活用できる)
-
- 指定したURLの外部ファイルをロードする。(ひたすらロードするのみ)
javascript:(function(urls,i,s){for(i=0;i<urls.length;i++){s=document.createElement('script');s.src=urls[i];document.body.appendChild(s)};})([/* "URL","URL",... */])
- 指定したURLの外部ファイルをロードしてから、内部コードを実行する。
- ブックマークレット内で外部のJavaScriptライブラリを利用したい場合。
javascript:(function(f,urls,i,s){urls=[/* "URL","URL",... */];for(i=0;i<urls.length;i++){s=document.createElement("script");s.src=urls[i];if(i==urls.length-1)s.onload=function(){f()};document.body.appendChild(s)}})(function(){/* 内部コード */})
javascript:(function(f,s){s=document.createElement("script");s.src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js";s.onload=function(){f(jQuery.noConflict(true))};document.body.appendChild(s)})(function($){/* 内部コード */})
javascript:(function(f,urls,i,s){urls=["//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"/* ,"URL","URL",... */];for(i=0;i<urls.length;i++){s=document.createElement("script");s.src=urls[i];if(i==urls.length-1)s.onload=function(){f(jQuery.noConflict(true))};document.body.appendChild(s)}})(function($){/* 内部コード */})