DOM ロード関連のイベントの発火順番を調べるテストを書きました。
TEST : DOM Load Event
上記テストでは、いくつかの手法を一つのファイルにまとめています。
IE で DOM ロードを検知するには、script defer を使うのが優れた方法だとおもいますが、document.write を使いたくない場合には、doScroll の動作検知を利用したり、setTimeoutを使わない cached htc を利用したりと多様なやり方があることが分かりました。
2007-12-12
TEST : DOM Load Event
IE/DOMLoad : cached htc
document.write を使わず、外部ファイルのダウンロードをしない、IE用の DOM ロードイベントを取得する方法を考えました。
DHTMLの ondocumentready イベントを利用します。
そのためには、DOM要素に behavior 属性で htc ファイルへの参照設定が必要となります。そこで、外部ファイルのダウンロードを不要とするために、ブラウザのキャッシュ機能の利用を考えて、javascriptファイルの拡張子を htc とします。そしてブラウザ判定でIEならば、拡張子が htc である javascript ファイル自身のURLをbehavior属性に渡します。これによりブラウザのキャッシュが使用され、新たな通信が不要となることが期待されます。
HTML :
<script src="http:// ... /onready.htc" type="text/javascript" charset="utf-8"></script>javascript :
onready(function(){ alert('how are you?'); }); onready(function(){ alert('fine! and you?'); }); onready(function(){ alert('me, too!'); });
onready.htc :
/* <?xml version="1.0" encoding="utf-8" standalone="yes"?> <public:component xmlns="http://www.w3.org/1999/xhtml" xmlns:public="urn:HTMLComponent"> <public:attach event="ondocumentready" onevent="element.init();" /> <!-- onready.htc : Cross-Browser DOMContentLoaded Event Listener with Cached HTC script hack for IE Copyright (c) 2007 latchet (http://juce6ox.blogspot.com/) Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) */ var onready = (function(){ var id, ua = navigator.userAgent.toLowerCase(), fs = [], i = 0, done = false, init = function(){ var f, j = 0; if(done) return; done = true; /*@cc_on document.documentElement.init = null; if(id != undefined) document.documentElement.removeBehavior(id); window.detachEvent('onload', init); document.onreadystatechange = null; /*@if(false)@*/ document.removeEventListener('DOMContentLoaded', init, false); window.removeEventListener('load', init, false); /*@end@*/ window.onload = null; while(f = fs[j++]) f(); }, /*@cc_on url = (function(e, ok){ while(e = e.lastChild) if(e.nodeName.toLowerCase() == 'script' && ok.test(e.src)) return e.src; })(document, /^[^?#]+\.htc$/), @*/ set = function(){ var ok; /*@cc_on document.documentElement.init = init; if(url) id = document.documentElement.addBehavior(url); window.attachEvent('onload', init); document.onreadystatechange = function(){ if(document.readyState == 'complete') init(); }; /*@if(false)@*/ if(/mozilla/.test(ua) && !/compatible|webkit/.test(ua) || /opera/.test(ua)) document.addEventListener('DOMContentLoaded', init, false); else if(/webkit|khtml/.test(ua) && (ok = /loaded|complete/)) (function(){ return ok.test(document.readyState) ? init() : setTimeout(arguments.callee, 0); })(); window.addEventListener('load', init, false); /*@end@*/ var old = window.onload; window.onload = (typeof old == 'function') ? function(){old();init();} : init; }; return function(f){ if(typeof f != 'function') return; if(done) return f(); if(!i) set(); fs[i++] = f; }; })(); //--></public:component>
参考資料
- window.onload - An Alternative Solution
- XML版HTCを書くための永久保存版テンプレートとbehaviorを使った時の実行順序、イベントなど - Ci.nsIZIGOROu - Mozilla 拡張機能勉強会
- TAKESAKO @ Yet another Cybozu Labs: Operaでも非同期リクエストが並列処理できる img-JSONP
- Shibuyajs24 JavaScript.GIF x LiveConnect » SlideShare
通常の外部 HTC ファイルをロードして ondocumentready イベントを設定する方法について。
HTCファイルのXML表現をはじめとする丁寧で詳細なまとめ。
ブラウザのキャッシュ機能を利用するアイデア。
拡張子が js でないファイルを script 要素でロードし javascript とみなすアイデア。
IE/DOMLoad : behavior expression
IEの独自実装による document.write を使わない DOMロード の検知方法を考えました。
CSSのbehaviorとexpressionを使います。
behaviorは画面描画のたびに頻繁に評価されます。
expressionを用いると、javascriptを記述できます。
上記を利用することで、Diego Perini氏による doScroll の動作チェックコードの頻度をあげるというアイデアです。
アイデアの中心となるコードは次のようなものです。
document.documentElement.style.setExpression('behavior', 'document.documentElement.polling()');
これを利用したDOMロードイベントのライブラリを公開します。
利用方法は、関数 onready の引数に、DOMロード後に実行したい関数を渡すだけです。
HTML :
<script src="http:// ... /onready.js" type="text/javascript" charset="utf-8"></script>javascript :
onready(function(){ alert('how are you?'); }); onready(function(){ alert('fine! and you?'); }); onready(function(){ alert('me, too!'); });
onready.js :
/* onready.js : Cross-Browser DOMContentLoaded Event Listener Copyright (c) 2007 latchet (http://juce6ox.blogspot.com/) Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) */ var onready = (function(){ var id, ua = navigator.userAgent.toLowerCase(), fs = [], i = 0, done = false, /*@cc_on oldExp, @*/ init = function(){ var f, j = 0; if(done) return; done = true; /*@cc_on if(oldExp) document.documentElement.style.setExpression('behavior', oldExp); else document.documentElement.style.removeExpression('behavior'); window.detachEvent('onload', init); document.onreadystatechange = null; /*@if(false)@*/ document.removeEventListener('DOMContentLoaded', init, false); window.removeEventListener('load', init, false); /*@end@*/ window.onload = null; while(f = fs[j++]) f(); }, set = function(){ var ok; /*@cc_on oldExp = document.documentElement.style.getExpression('behavior'); document.documentElement.polling = function(){ try{ document.documentElement.doScroll('left'); }catch(e){ return setTimeout(document.documentElement.polling, 0); }; document.documentElement.polling = null; init(); }; document.documentElement.style.setExpression('behavior', 'document.documentElement.polling()'); window.attachEvent('onload', init); document.onreadystatechange = function(){ if(document.readyState == 'complete') init(); }; /*@if(false)@*/ if(/mozilla/.test(ua) && !/compatible|webkit/.test(ua) || /opera/.test(ua)) document.addEventListener('DOMContentLoaded', init, false); else if(/webkit|khtml/.test(ua) && (ok = /loaded|complete/)) (function(){ return ok.test(document.readyState) ? init() : setTimeout(arguments.callee, 0); })(); window.addEventListener('load', init, false); /*@end@*/ var old = window.onload window.onload = (typeof old == 'function') ? function(){old();init();} : init; }; return function(f){ if(typeof f != 'function') return; if(done) return f(); if(!i) set(); fs[i++] = f; }; })();
参考資料
- Diego Perini : IEContentLoaded - An alternative for DOMContenloaded on Internet Explorer
- msdn : behavior Property
- msdn : setExpression Method
- Jesse Skinner : addDOMLoadEvent - The Future of the Web
このアイデアを思いついてコードを書き終えたころに、細かい違いはありますが、 同じようにbehaviorとexpressionを使うアイデアを、doScrollハックの考案者であるDiego Perini氏がjQueryのメーリングリストに投稿していたことを検索の結果、知りました(笑)
Nabble - Re: document.ready firing too early within iframe
その時は正直、すこしがっくりしましたが、気付かなかったからこそ取り組めた面もあるのかもしれません。コードを整えつつ、その後、新しい別の方法が無いか再び試行錯誤し、(自分にとって)新しいアプローチを見つけました。document.writeは使わず、IEのDHTMLのondocumentreadyイベントを利用しつつ、新たな通信をしなくてもすむとおもわれる方法です。
IE/DOMLoad : cached htc
window.onload Problem
javascript の window.onload イベントはページの解析、画像などのファイル読込が全て完了してから発生するようです。
そのため、ロードイベント前の文書の解析後に迅速にページのDOM要素に対してjavascriptで操作を行ないたいという要求がおこるのは、ユーザーの利便性を考慮すると当然といえそうです。
Ajaxという言葉が注目され、Googleによるjavascriptを活用したアプリケーションが登場し、
回線速度の向上、インフラ整備、ブログ作成者・閲覧者の増加、ウェブAPIの公開、
マッシュアップサイトが数多く登場しています。
そのような背景の中でjavascriptによる DOM ロードイベント検知の方法もいろいろ探究されてきたようです。今までの流れを書き出してみました。
- 1993/04/30 : Tim Berners-Lee , CERN - World Wide Web 無料開放
- 1995 : Brendan Eich - JavaScript誕生
- 1998/10/01 : W3C - DOM1仕様勧告
- 1999/03/18 : Microsoft - IE 5 リリース
- 2004/04/01 : Google - GMail 告知
- 2005/02/08 : Google - Google Maps 告知
- 2005/02/18 : Jesse James Garrett - Ajax 提唱
- 2005/09/24 : Dean Edwards - DOMロード問題
- DOMContentLoaded ( Mozilla/Firefox )
- script defer ( IE : 外部 javascript ファイル読込あり )
- 2005/09/25 : Dean Edwards - DOMロード問題
- ondocumentready ( IE : 外部 HTC ファイル読込あり )
- 2005/09/30 : Tim O'Reilly - Web 2.0 提唱
- 2006/04/20 : livedoor - livedoor Reader リリース
- 2006/06/08 : Matthias Miller - DOMロード問題
- script defer ( IE : 外部 javascript ファイル読込なし )
- 2006/06/15 : John Resig - DOMロード問題
- document.readyState ( Safari )
- 2006/06/20 : Opera 9 リリース
- DOMContentLoaded ( Opera )
- 2007/06/28 : Diego Perini - DOMロード問題
-
The window.onload Problem - Solved!
-
window.onload - An Alternative Solution
-
The window.onload Problem Revisited « Out of Hanwell
-
window.onload (again)
-
Your Web, Your Choice: Opera 9、これまでにない操作感を
枯れた技術が活用され、以前は想定されなかった問題が解決されていく様子に見えます。
HTML5草案では、DOMContentLoadedイベント(FirefoxとOperaは実装済)が提案されているそうです。
将来、SafariとIEにも採用されたら多くの人に喜ばれそう。
2007-11-17
CSS/DOM : 動的にスタイルシートへルールを追加する
スタイル属性を動的に一括して設定します。
引数にはセレクタとルールを指定します。
//使用例 //addCSSRule(selector, declaration); addCSSRule('div.test', 'width:200px; height:100px;'); addCSSRule('div.test', 'background:#ccc;');
var addCSSRule = (/*@cc_on!@*/false) ? (function(sheet){ return function(selector, declaration){ sheet.addRule(selector, declaration); }; })(document.createStyleSheet()) : (function(sheet){ return function(selector, declaration){ sheet.insertRule(selector + '{' + declaration + '}', sheet.cssRules.length); }; })((function(e){ e.appendChild(document.createTextNode('')); (document.getElementsByTagName('head')[0] || (function(h){ document.documentElement.insertBefore(h, this.firstChild); return h; })(document.createElement('head'))).appendChild(e); return e.sheet; })(document.createElement('style'))) ;
資料
2007-11-13
XPath/DOM : javascriptファイルの呼び出し元script要素を取得する
XPathを用いると、きめ細かく要素を指定できるようです。
たとえばamachangさんによるscript要素取得コードは以下のようなXPath式で表現可能です。
createElementで動的に生成されたscript要素を、その時点でのDOMツリーの末尾以外の任意の位置にappendChildしている場合には要素を取得できずにnullを返すか、ブラウザが解析中にたまたま末尾に存在する別のscript要素が返されます。
静的にHTMLで記述されたscript要素についてはその心配はありません。
var currentScript = !!(document.implementation && document.implementation.hasFeature && document.implementation.hasFeature('XPath', '3.0')) ? document.evaluate( '//*[local-name()="SCRIPT" or local-name()="script"][not(ancestor-or-self::*[following-sibling::*])]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue : (function(e){ return (!e) ? null : (e.nodeName.toLowerCase() == 'script') ? e : arguments.callee(e.lastChild); })(document) ;
探索対象文書(XML, HTML)、ブラウザの種類によってXPath式で判定可能な要素名の大文字・小文字の種別に影響があるようです。
たとえば、このブログのアドレスバーで以下のブックマークレットを実行した場合、Firefox2とOpera9で挙動が異なります。IEは現時点でXPathをサポートしていないため両方ともエラーとなります。
XPathでscript要素を抽出し、成功したらnodeNameを表示します。
■大文字(SCRIPT)でXPathにより要素抽出
○ Firefox2
× Opera9
× IE
javascript: try{alert(document.evaluate('//*[local-name()="SCRIPT"]',document,null,9,null).singleNodeValue.nodeName);}catch(e){alert(e);};
■小文字(script)でXPathにより要素抽出
× Firefox2
○ Opera9
× IE
javascript: try{alert(document.evaluate('//*[local-name()="script"]',document,null,9,null).singleNodeValue.nodeName);}catch(e){alert(e);};
資料
2007-11-08
XPath/DOM : Class属性による要素抽出 - クロスブラウザ document.getElementsByClassName
javascriptの優れたライブラリが近年続々と公開されています。
幸いな事にprototype.js、jQuery、そして最近ではさらにamachangさんによるライブラリによってjavascriptによるDOM要素抽出が気軽に見通しよく記述することができる環境が整っているようです。
車輪の再実装ですがタグ名とクラス名を指定してDOM要素抽出を行なうという用途のための小さなコードを書きました。既存ライブラリを使わない小さなサイズのスクリプトでちょっとしたDOM操作のために。
使い方はgetBoxes(klass, tag) でクラス属性値klass、タグ名tagのDOM要素を配列にして抽出します。tagを省略すると、指定されたクラス属性値を持つ全てのタグを抽出します。
たとえば、getBoxes('test', 'div') では、testをクラス属性値に持つタグ名divの要素を配列で返します。
■追記:2007/11/12
getBoxes(klass, tag, context) のように第3引数にDOM要素を指定すると、その要素と子孫要素が探索対象範囲となる機能を追加しました。第3引数を指定しなければページ内の全要素が探索対象となります。
XPathを使用する場合、XPath式とnamespace resolver(ここでは、nullです)から生成されるオブジェクトを保存し再利用するようにしました。
var getBoxes = (/Firefox\/(\d+)/.test(navigator.userAgent) && RegExp.$1 > 2) ? (function(){ var toChangeCase = (document.documentElement.nodeName == 'HTML') ? 'toUpperCase' : 'toLowerCase'; return function(klass, tag, context){ var boxes = (context || document).getElementsByClassName(klass); return (tag && (tag = tag[toChangeCase]())) ? Array.filter(boxes, function(e){return e.nodeName == tag;}) : boxes; }; })() : (document.implementation && document.implementation.hasFeature && document.implementation.hasFeature('XPath', '3.0')) ? (function(){ var toChangeCase = (document.evaluate('//*[local-name()="HTML"]', document, null, XPathResult.BOOLEAN_TYPE, null).booleanValue) ? 'toUpperCase' : 'toLowerCase', cache = {}; return function(klass, tag, context){ var boxes = [], key = (tag || '*') + ':' + klass, exp = cache[key] || (cache[key] = document.createExpression('//*' + (tag ? ('[local-name()="' + tag[toChangeCase]() + '"]') : '') + '[contains(concat(" ",@class," ")," ' + klass + ' ")]', null)), nodes = exp.evaluate(context || document, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null), node, i = 0; if(nodes) while(node = nodes.iterateNext()) boxes[i++] = node; return boxes; }; })() : function(klass, tag, context){ var boxes = [], regex = new RegExp('(?:^|\\s)' + klass + '(?:\\s|$)'), i = 0; /*@cc_on if(@_jscript_version > 5.5 || @_mac || tag || !context){ @*/ var nodes = /*@cc_on (@_jscript_version < 5.6 && !@_mac && !tag && !context) ? document.all : @*/ (context || document).getElementsByTagName(tag && tag.toLowerCase() || '*'), node, j = 0; if(nodes) while(node = nodes[i++]) if(regex.test(node.className)) boxes[j++] = node; /*@cc_on }else{ (function walk(e){ if(e.nodeType == 1 && regex.test(e.className)) boxes[i++] = e; e = e.firstChild; while(e){ walk(e); e = e.nextSibling; }; })(context); };@*/ return boxes; };
資料
- getElementsByClassName on Gecko: Days on the Moon
- DOM:document.getElementsByClassName - MDC
- HTML 5 Working Draft
Firefox3で導入されるgetElementsByClassNameの仕様・特徴について。 -
John Resig - getElementsByClassName Speed Comparison
jQuery作者John Resigさんによるクラス属性値を指定した各種要素抽出方法の性能比較。 Firefox3で実装されるdocument.getElementsByClassNameが現状で最も高速。次いでXPath、DOMの順とのこと。 - FirefoxのXPathEvaluator
Firefoxに実装されているXPathEvaluatorの速度計測。 - google-code-prettify - Google Code
このエントリーで使用しているjavascriptによるシンタックスハイライト・ライブラリです。 - 日本の伝統色 和色大辞典 - Japanese Traditional Color Names
こんなに多くの呼び名があることに驚きです。シンタックスカラーの参考にしました。 - Latest topics > CSS3セレクタとXPathでの表現の対応表 - outsider reflex
丁寧に詳しい説明です。 - XPath, $X function, NSResolver < 16 < March < 2006 < nulog, NULL::something : out of the headphone
XHTMLでの名前空間、XPathのNamespace Resolverについて。
2007-07-06
SHA256を利用したパスワード生成ブックマークレット
worris' ドメイン名でMD5なパスワードを作るBookmarklet
上記エントリーに触発されてパスワードを自動生成するブックマークレットを作成しました。
javascript:
void(new function(){
var b=document.body,f=arguments.callee,s;
if(!b)return setTimeout(f,100);
s=document.createElement('script');
s.type='text/javascript';
s.charset='utf-8';
s.src='http://juce6ox.googlepages.com/pass.js?true,1,8';
b.appendChild(s)
});
上記コードをクリックすると、ブックマークレットが起動します。
右クリックで保存できます。
■特徴
- サイトのドメイン名を利用
- SHA256ハッシュを利用
- 数字と大小の英字を使用したパスワードを生成
- パスワードにはオプションで2文字まで利用する記号を指定可能($_+=?<>など)※ デフォルトでは記号使用なし
- パスワード欄に入力済みの文字列を利用
- パスワード欄に自動書き込み
- 生成されたパスワードの表示・非表示の設定
- パスワードの文字数設定 ※最大43文字
■使い方
- パスワード欄にキーワードを入力
- ブックマークレット実行
- ドメイン名とキーワードを元に生成されたパスワードが自動で入力されます ※パスワード入力欄がある場合に
ブックマークレット内の s.src の値を書き換えることで設定できます。
pass.js?[v0],[v1],[v2],[v3],[v4] のように?以降にカンマ区切りで書き込みます。
- [v0]
パスワードのダイアログ表示[true]・非表示[false]。
デフォルト値=[true] - [v1]
SHA256ハッシュの何文字目から文字列を抽出するか指定。[0以上の整数]
デフォルト値=[1] - [v2]
パスワードの文字数
デフォルト値=[8] - [v3]
記号2文字を指定。[/=, $_, +-, <> など]
デフォルト値=[01] - [v4]
内部処理でドメイン名と入力文字を結びつける際の間の文字列
デフォルト値=[ ]
■お願い
このブックマークレットは2つのjavascriptファイルをダウンロードします。- sha256.js (from Schwarz Lanzenreiter )
- pass.js
SHA256ライブラリにつきましては作成者の方のサイトをご参照の上でのご利用をお願いいたします。
●ブックマークレット 変更箇所
⇒ s.src = 'pass.jsの保存場所' (http://~、file://C:/~)
⇒ pass.js?[設定項目をカンマ区切りで]
s.src='http://juce6ox.googlepages.com/pass.js?true,1,8';
●pass.js 変更箇所
⇒ u = ['pass.jsの保存場所', 'sha256.jsの保存場所']
⇒ c = 'sha256.jsの文字コード'
var u=['http://juce6ox.googlepages.com/pass.js', 'http://user1.matsumoto.ne.jp/~goma/js/sha256.js'], c='sjis',
■作成にあたって参照したサイト一覧
-
JavaScript でハッシュアルゴリズム
javascript版SHA256ライブラリ。
このブックマークレットで利用しています。 -
TAKESAKO @ Yet another Cybozu Labs: Operaでも非同期リクエストが並列処理できる img-JSONP
img要素を利用するOperaでのJSONPの仕方。 - 最速インターフェース研究会 :: [Ajax] JSAN構想とリモートデータの取得とUserJavaScript
JSAN DEMO
動的ロードを監視するwait関数。 -
syntaxhighlighter - Google Code
コードをシンタックスハイライトするjavascript/cssライブラリ。
2007-07-02
2007-06-08
Webスクラップ、メモ:ScrapBook
Webスクラップ、メモ:TiddlySnip
閲覧しているウェブサイトをTiddlyWikiの記事(Tiddler)にメモするための FireFox拡張を見かけたので導入。
同名のTiddlerが存在しているときに、 ダイアログが表示されて、- 記事名称の変更[Rename]
- 記事に追記[Append]
- 記事を上書き[Overwrite]
TiddlyWiki, d3 用ブックマークレット
閲覧しているサイトを、d3などTiddlyWikiでメモするためのブックマークレットを下記サイトを参考に導入しました。 FireFox用です。IEでは動きません。
TiddlyWiki用ブックマークレット2007-06-07
GTDツール
GTDという仕事術の話題を以前からよくウェブや書店で見かけました。 それに関連するアプリケーションについての言及もはてなブックマークなどで目にします。 そんな経緯から先日、JavaScriptでつくられたWikiツールを調べました。
上記はいずれも、TiddlyWikiというHTMLファイル一つからなるWikiとその改良版。 それぞれに特徴がありますが、d3の機能が個人的には使いやすそうに感じられました。 メモ用ツールにXTMemoというソフトを利用しています。 このソフトはとても便利。 タグ付けも同じ題名のメモも作れるし、タグや本文の検索も簡単。 現状ではまだd3はほとんど使っていない。2007-06-06
Google Mashup Editor
先日、グーグルから様々な発表がされました。 ・Google Mapplets ・Google Gears ・Google Mashup Editor その中でも、Google Mashup Editorに関する記事がまだウェブ上ではとても少ない。 現在、サービス利用できる人が限定されているためというのもあるのかな。 Google Codeにリファレンス等あり。http://code.google.com/gme/index.html おもしろそう。 googlemashups.comのサブドメインに作成したコードがアップロードされるようだ。 Yahoo! Pipesとの相違を考察するエントリーを、今後見かけそうだ。 Google Mashup Editorはサーバサイド、Google Gearsはローカル・クライアントサイドで活躍と相互に補完的な役割を活かすような双方を用いたアプリケーションが現われるのかな。Flash9,Apollo,Google Gearsの連携する魅力的なアプリ、あっと驚き、くすっと笑いをおこすようなアイデアの登場が夏以降ありそう。楽しみ。
2007-06-05
Google Gears
先日、Googleからオフライン・オンラインでブラウザ・ベースのアプリケーションを動作させる仕組みを提供するGoogle Gearsが発表された。 ローカルサーバ、データベース、ワーカープールと呼ばれるマルチスレッド機構が搭載されているようだ。 AdobeのApolloと連携したアプリケーションが今年、続々と登場するのだろう。 どのようなアイディアが世間を驚かすのか楽しみ。
ねこについて
ねこはやはり寝息をたてる。 どうやらいつもじゃない。 深夜、ねこの寝息に気付いて聞いていたら、しばらくしたらやんだりした。 どういう条件で、寝息をたてたり、やんだりするのか、良く分からない。 地震の起こる30分か1時間位前に、近くにやってきて呼びかけるようにこちらを向いて声を上げた。 えさの催促ではなかった。呼びかけのあと、しばらくしたらどこかへ行ってしまったから。 地震があったときにはじっと昼寝をしていた。 振り返っておもいだすと、もしかすると、あれは地震を感知する動物の異常行動と呼ばれるものなのだろうか。