Cross Browser のための DHTML

怪談! Cross Browser

〜 JavaScript1.2+ の食えない事情 〜

DHTML の必須ともいえる JavaScript。 然し、 Microsoft の JScript と Netscape の JavaScript では DOM構造は全くと言って良いほど両者は異なり、文法すら微妙に異なる部分があります。

ここでは、主に Cross Browser DHTML を作成する上での JavaScript の記法に関する問題や 障害になった( なっている )動作、できれば、これらの回避策も含めて記すことにします。


目  次

よくやる間違い
こんな間違いをするとアセります
DHTML のやってはいけないこと
こんな記述をすると悲しい目にあいます
コンパイラ
コンパイラ絡みの問題(かな?)
NN4 の不思議
NC4 のバグとも呼べない不思議な動作 !?
JSファイル利用の JavaScript
NN4 で JSファイルを利用する場合( 特に Win98 )は気をつけよう...
外部ファイルを使用したレイアの功と罪
NN4 の外部ファイルを指定したレイアは良い?それとも命取り?
DHTMLのパフォーマンス
IE4 vs. NC4、あなたはどちらが速いと思います?
onLoadイベント
onLoad イベントは本当に document のローディング完了を意味する?
innerHTML と insertAdjacentHTML
IE4 の innerHTML と insertAdjacentHTML、どう使う?
レイアと document.writeln
NN4 のレイアに document.writeln すると...
ウィンドウサイズとレイアウト
ウィンドウの中心って微妙にズレるんですよね。
DIV のサイズ
あなたは IE4 のレイアのサイズを何で見ます?
タイトなレイアの生成
文字列を含むタイトなレイア、作ることできます?
背景色の変更
NN4 で文字などを含むレイアの背景色を変更するには苦労する
Number オブジェクト
IE4 では Numberオブジェクトのプロパティを信じてはいけません。
オブジェクトと prototype プロパティ
IE4 のオブジェクトは Object ではない!?
見えないレイア
IE4 のバグは油断ならない
レイアの表示制御手法
あなたは move派? それとも visibility派?
write メソッドのパフォーマンス
NC4 の writeメソッドは遅いって知ってました?
writeメソッドとスタイル定義
レイアに直接 write する時スタイルを指定したいよね?
タイマ管理の不思議
IE4/5 のタイマ管理って、やっぱり手抜きじゃあないの〜?
setTimeout と setIntervalメソッド
IE4 の setInterval メソッドってヘンな仕様
mouseover と mouseout
IE4 のイベントハンドリングは不親切
mousedown と mousemove
IE4 vs. NC4、ドラッグ処理はここまで違う
mousedown,mouseup そして click
mousemove と click イベントを共存させると困ります
onmousemove と document.write
mousemove を登録したレイアに write すると困ります

よくやる間違い

<script langauge="JavaScript1.2"><!--
<script language="JavaScrip1.2"><!--

language のスペルをミスると指定しないのと同じ。

script のスペルをミスると scriptタグを記述しないのと同じ(笑)。

document.all.div.innerHTML  = '<H1>';
document.all.div.innerHTML += '大きくする文字';
document.all.div.innerHTML += '</H1>';

innerHTML プロパティはタグのペアになる単位で書き込まないとタグを補完されてしまう。

上のスクリプトの結果は次のようになる。

<H1></H1>大きくする文字</H1>

※ 最後の </H1> はなくなるかもしれない

document.all.div.onMouseOut = func;

IE4 では onmouseout と全て小文字でなければ認識しない。
( 因みに NN4 はどちらでもOK )

document.all.div.style.zIndex=0;

NN4 では最上位のレイアになるが、IE では単に最下位のレイアになるだけ。

※ 因みに MS の IE4用 InetSDK には zOrder というメソッドの記述があるが 実際にはそんなものはない。 ただ、エラーメッセージが見られるだけ。

因みに、IE5用の InetSDK を見ると、そんなメソッドの痕跡すらなくなっている(笑)。

div.style.pixelLeft+=dx;

style オブジェクトの pixelLeft, pixelTop 等は Number型として定義されている。

Numberオブジェクトは ECMA-262 で定義され、実数も扱えることになっている。
だからといって、pixelLeft に実数を加減算しても結果は整数になるので これらの処理を繰り返すと誤差の山になる(笑)。

※ pixelLeft はスタイルの定義からの演算値なので pixelLeft に値を設定すると left属性に変換・設定し、その後 pixelLeft属性を再設定する結果かもしれない。

... ってことはスタイルで実数値が扱えないことになる。 f(^^;


DHTML のやってはいけないこと

document.layers.div.document.write( 
            '<style type="text/css"...' );

IE4 では無視されるだけだが、NN4 では異常終了する確率が高い(*1)。

*1 フォントキャッシュが壊れれば、更に追求に時間がかかる( Windowsの場合 ^^; )

// スクロールに合わせてレイアを移動
style.pixelLeft+=dx;
style.pixelTop +=dy;
scrollBy(dx,dy);

NN4 では正常に動作するが IE4 ではレイアが表示更新されない。

因みに、こんな風にすると動く。

style.pixelLeft+=dx;
style.pixelTop +=dy-1;
scrollBy(dx,dy); // 移動量が異なる
scrollBy(dx,dy); // 最初にスクロールをする
style.pixelLeft+=dx;
style.pixelTop +=dy;

※ いったいどーゆープログラミングをしているんじゃい。 (..#)


コンパイラ

JavaScript って、こう書きますよね。

<script language="JavaScript"><!--
// --></script>

最初は「ナルホド」って思ったんですが SCRIPTタグを解釈しないブラウザのための コメントの開始には「//」がなくって、最後には「//」の記述を入れるんでしょう?

因みに IE では「//」がなくてもエラーにはなりませんが、 NN4 だとしっかりエラーになります。 f(^^;

これ。 NN4 のコンパイラ( 現在では parser と呼ぶんだろうか? )が HTML のタグ構造を論理的に解析しているんじゃない、ってことかなぁ

これが理由かどうかはわかりませんが、文法的に間違っていない ( つまり、通常は正常に動作する )スクリプトが、 ローディング時にエラーを発生するケースがあります。

一説にはスクリプト中に記述されている「</...>」タグが 「</script>」タグと解釈されたためという意見もありますが、 実際にはこれだけではないようです。

どうやら、単純にスクリプトや HTML の構造が複雑になると発生する傾向があります。

※ 因みに、特に DIVタグはこの傾向が強いようですが、コンパイラの問題と言うより、 読み込み動作のバッファ管理やタイミングとりの問題のような気がします。


NN4 の不思議

NN4 は 4.06 から内部コードを「ECMA準拠」、つまり UNICODE になったようですが、 ステータス領域の文字化け問題(*1)のような不具合が出ているようです。

*1 NN4.5[Ja] で修正されました

「\」文字問題もその1つです(*2)
「\」文字問題とは、この文字を JavaScript でハンドリングすると 「\」記号に変換されてしまう問題で、詳細は以下の通りです。

*2 この情報は高橋さんの「JS-ML」からのものです

と言うことで、どうも「\」文字を作為的に変換しているようです(*3)

これは「\」文字が UNICODE のベンダ依存文字として割り当てられているため、 Netscape社では「\」文字に変換しているらしいことによります。

残念ながら、この問題の回避方法は思いつきません(*4)
回避方法を発見された方はご連絡下さい。

*3 だとすれば、ひょっとすると[en]版では発生しないかもしれません

*4 このために、日本式顔文字が JavaScript で表現できない危機に陥っています(笑)


JSファイル利用の JavaScript

JSファイル( src="*.js" )をあまり利用しないので気がつかなかったのですが、 NN4( 少なくとも NN4.7 )では起動時にページを読み込む場合と 起動後に読み込む場合では動作が異なる場合があるようです。

例えば、JSファイルに「img=document.images;」と記述した1行があると装丁してください。

このファイルを HTML で指定し、onLoadイベントでアクセスすると NN なら 「[object ImageArray]」といったレイア配列が設定されていると想像しますが、 このページを指定して起動すると「undefined」になります。

この現象を調べてみるとどうも「通常のブラウザ」として登録されていない場合に発生し、 確認ダイアログを表示している間に JSファイルの一部を実行するために未生成の document 以下のプロパティをアクセスすることにより「undefined」になるようです。

回避方法としては、しょうがないので JSファイルにこのような記述をしないか、 あるいは問題となる部分を関数化して onLoad イベント時にメインから呼び出すこと くらいでしょうか。 f(^^;

※ 因みに、この現象は NN3( NN3.02 )では発生しません。


外部ファイルを使用したレイアの功と罪

NN4 のレイアは外部ファイルをソースとして指定できます。

これは、Platform Preview版の頃、IE の IFRAME に代わるものとして説明されていました。

確かに、NN の document構造から考えると納得のいくメカニズムに思えます。

しかし、レイア内で定義された関数や変数の扱いを考えると、 この document.構造が充分に活かされている実装とは言えません。

それにもかかわらず、レイアで外部ファイルを扱えることは 充分なメリットが出てくる可能性があります。 それは JavaScript からサーバ側の資源を直接アクセスする手段としてのメリットです。

これができると、例えば「micro chat」や「訪問者の足跡2」のように サーバとの通信をページ更新なし(厳密にはウソ)で行えます。
これはフレームやウィンドウを使用しても同様のことが可能ですが、 フレームに関する不具合やウィンドウクローズに対する配慮が不要になります。

とまあ、ここまでがメリットですが、 NN の load メソッドにはローディング手順上の問題が内在しているようです。

load メソッドと Layer コンストラクタの問題

レイアに対して外部ファイルをローディング中にレイアを生成しようとすると コンストラクタ内で処理が中断されるようです。 ( エラーは発生しません。ただ単に中断されます。 )

回避策としては、load メソッドはレイアの生成後に行うか、 ローディング処理中はレイアの生成をロックするしかなさそうです。

load メソッドと unLoad の問題

レイアに外部ファイルをローディング中に ローディング元のページに onUnload イベントが発生すると ブラウザ( NN4.51 )が異常終了することがあります。

ローディング中なら、ローディング完了を待ちたいところですが、 こればっかりは、回避策を思いつきません。 因みに、「訪問者の足跡2」では状態値を送信するタイミングを onUnload イベントにしていましたが( 今も痕跡はあります )、 マウスクリック時に変更しています(*1)

注 この点 IE はそう言う問題はありませんが、 別の理由で NN と同じタイミングにしています。 f(^^;

こう考えると、メリットを採るかデメリットを採るか、悩むところですね〜


DHTML のパフォーマンス

IE と NN4 とを比べてみると IE の実効性能が悪いことに気がつきます。

「JavaScript の食えない事情」でも JavaScript の実効性能については 色々と言及していますが、実際に様々な要因があるようです。

例えば IE はページ表示時には HTMLファイルとスクリプトの間で 若干の並行処理を行っていますが、 一旦ローディングが完了してしまうと描画処理とスクリプトの実行は並列には処理をしないようです (*1)

※ 実際に確認したい方はこちらにサンプルを用意してあります。

1. 描画処理の確認( body編 )
2. 描画処理の確認( body編 - NN4 は不可 )
3. 描画処理の確認( dynamic編 )

また、ここで言う「並行処理」とはプログラム構造上の並行処理ではなく、 単にスクリプト実行中に描画処理がされることを意味しています。

JavaScript の実行性能自体は NN4 より IE の方が現在では速くなっているようですが、 この並行処理性のために描画が遅く感じます。

そこで、確認のために100個のレイアを生成して実行性能を見てみる (*1)と以下のようになります。

ブラウザbody1関数複数回
IE411066010550
IE5.51103805870
IE61103905330
NN43802205490

*1 測定は次のものを利用して行っています。

レイアの実行性能( body編 )
レイアの実行性能( dynamic編 )

表の「body」は上の「body編」の、 「1関数」は「dynamic編」で「1関数で生成」の、 「複数回」は「dynamic編」で「複数回に分けて生成」の結果を示し、 単位は全て msec です。

つまり、HTMLで直にレイアを記述した場合は NN4 の3倍程度速いのに 一旦ロードしてしまうと IE6 でも倍近く、分割してほぼ同じ結果になっています。

この時間差はどこから来るのでしょう?

ロード完了後のスピードが落ちるのは、多分、 レイア生成により body部の HTML が変わるため、 この差を DOM に反映させる手間(*2) がかかっているように見えます。

まあ、 W3C の DOM 構造は IE の DOM 構造をベースにしているようで、 決して性能面を考慮したものには見えません。

1関数で生成する方が遅いのは、 描画処理をサボるための仕組みがアダになっていると思われます。

この点では NN6 は更に遅いと思われます(*3)

*3 実は NN6 も測定したのですが、 レンダリングエンジンの作成もとである Mozilla の正版が現段階ではリリースされていないので あえて割愛しています。 f(^^;


onLoadイベント

DHTML でレイアを使用してアクティブなドキュメントを作成する場合、 document.write メソッドでレイアを生成し、 onLoad イベントでそのレイアの位置を調整するような処理を記述することがあります。
しかし、NN4 では( 他は未調査 )この処理に注意が必要のようです。

つまり、BODYタグ内で document.write メソッドで生成したレイアが onLoadイベント内で正常にアクセスできないケースがあるようなのです。

例えば、この Tip集の DHTML版のタイトルページですが、 下部にある copyright 表示は document.write メソッドで生成し onLoad イベントでこのレイアをハンドリングして中央下部に配置する処理をしていました。

実際には殆ど動作するものの、時々このレイアの垂直位置が上部になる現象が発生しています。 これはレイアオブジェクト自体は生成されているものの、 レイアサイズなどの情報が未設定の状態にあることを示唆しています。

この理由として考えられることは document.write メソッドで出力したものの生成処理は本体の処理とは非同期で生成し、 onLoad イベント自体とも同期していないためと思われます。

このように仮定すると、通常は動作するものの、何らかのタイミングで 「div has no properties」などのエラーが発生することも得心できます。

この現象を防ぐには、onLoad イベントで実行する処理を onLoadイベントで setTimeout(0) で一旦ブラウザに制御を渡してから行うと かなり改善します。

それより、確実なのは onLoadイベントでハンドリングするレイアは onLoadイベント内で直接生成する方法でしょう(*1)

*1 問題がタイミングなだけに、確認手段がありません f(^^;


innerHTML と insertAdjacentHTML

IE のオブジェクトには使い方のよくわからないプロパティやメソッドがあります。
#> 単に英語が苦手だからかも f(^^;

innerHTML と insertAdjacentHTML もその1つです。

例えば、レイアをダイナミックに生成しようとして以下のような記述をします。

div.innerHTML += '<div id="d"><\/div>';
document.all('d').style.backgroundColor = 'white';

この記述は実際に動作します。

が、次のスクリプトは正常には動作しません。

div.innerHTML += '<div id="d1"><\/div>';
var div1 = document.all('d1');
div.innerHTML += '<div id="d2"><\/div>';
var div2 = document.all('d2');
div.style.backgroundColor = 'white';

どうやら、innerHTML に対して「+=」で追加したとしても innerHTML の全てのドキュメントが再評価され、 オブジェクトが再構築されるためのようです。

*1 "innerHTML += s;" の記述は "innerHTML = innerHTML + s" と 解釈され、実行されるのでしょう

従って、次のようにすれば正常に動作します。

div.innerHTML += '<div id="d1"><\/div>';
div.innerHTML += '<div id="d2"><\/div>';
var div1 = document.all('d1');
var div2 = document.all('d2');
div1.style.backgroundCOlor = 'white';

しかし、このような使い方は危険です。

なぜなら再構築される前に取得したレイアオブジェクトも レイアオブジェクトとして評価されるので(*2) このオブジェクトに対する操作は無視されるか、 意味不明のエラーメッセージが発生するため、デバッグに時間がかかります。

*2 再構築された後はこのオブジェクトは、いわば、幽霊的存在になります

この問題を回避するためには、代わりに insertAdjacentHTML メソッドを使用します。

div.insertAdjacentHTML('BeforeEnd','<div id="d1"><\/div>');
var div1 = document.all('d1');
div.insertAdjacentHTML('BeforeEnd','<div id="d2"><\/div>');
var div1 = document.all('d2');
div1.style.backgroundColor = 'white';

*3 なんのために innerHTML があるのでしょうかね〜
たぶん、 "+=" ではなく "=" で置き換える場合にのみ意味があるのでしょう。


レイアと document.writeln

例えばレイアに表示している内容を変えようとして以下のコードを書きます。

div.document.open('text/html');
div.document.writeln(
    '<img src="star.gif" width=3 height=3>');
div.document.close();

これ、一見正しく動きそうに見えますが、実は NN4 では思ったように表示されません(笑)。

理由は簡単。レイアに出力されている内容がこんな内容なので。

<div
><img src="star.gif" width=3 height=3
>\n</div>

つまり、改行コードがあるため NN4 では表示されない改行コードの高さが設定される(*1)ためです。 f(^^;

だから、対応策は改行コードをいれない( document.write を使用する )です。

*1 IE は改行コードがなくっても高さが設定されちゃいますけどね(笑)。


ウィンドウサイズとレイアウト

例えば、TABLE などをタグで中央に配置した場合とウィンドウサイズを基に 同じサイズのレイアを JavaScript で中央に配置した場合で、 同じ位置になると思います?

// HTML での記述
<div align=center>
<table width=100 height=100 cellspacing=0 cellpadding=0>
<tr><td></td></tr>
<table></div>
// JavaScriptでの記述
var div=initDivSize(getDivFromName('tb'));
moveDivTo(
  initDivSize(div,
  Math.round((getWindowWidth()-getDivWidth(div))/2),
  getDivTop(div));
// 'tb' は同じサイズの TABLE を含む absolute なレイアの id です
// getDivFromName,initDivSize,moveDivTo,getWindowWidth,
// getDivWidth はライブラリ集で紹介している関数です

少なくとも NN4 では違います。

計算して配置した場合、少し( 8pixel程度 )左にずれます。

これは、タグで中央に配置した場合はスクロールバーを含まない領域の中心になるのに対して、 JavaScript で取得するウィンドウサイズ(innerWidth,innerHeight)は スクロールバーを含む値であるためです*1

*1 無理矢理スクロールバーの幅を設定しちゃおうかとも思ったけど、 この幅はプラットフォーム依存なのよね〜 (-_-;


DIV のサイズ

DHTML でスクリプトを組もうとすると、必ずって言うほど必要なのがレイアの位置とサイズ。

NN4 ならレイアのサイズはレイア自体のサイズ(div.clip.width/height)と 含まれるドキュメントのサイズ(div.document.width/height)の2種類だけど、 IE は複雑で、しかもバージョンや HTMLの記述により異なる部分もあります(笑)。

プロパティIE4IE5IE6
div.offsetWidth/Height
div.clientWidth/Height
div.scrollWidth/Height
div.style.width/Height(*1)(*1)
div.style.posWidth/posHeight××
div.style.pixelWidth/pixelHeight××
div.currentStyle.width/height(*2)×(*3)

*1 IE5 からは style オブジェクトはインラインスタイルを反映し、 サイズ調整された場合でも反映されません。

*2 currentStyle は IE5 から新設されたオブジェクトで、 現在適用中のスタイルが設定されます。

*3 W3C標準準拠モード(?)では自動調整された場合 "auto" となり 値が設定されません。
#> 実は W3C標準モードでなくっても設定されたりして... f(笑;

まあ、スクリプト上で扱う場合は pixel単位の値が必要なケースが殆どでしょうから、 style関連のオブジェクトは変更時以外あまり使用できないかもしれません(*4)

では、レイア配下の各プロパティなら大丈夫かと言うと、 clip 指定されている場合などを考えると見かけのサイズではないので、 常に信頼できるわけではありません。

*4 正直言って、W3C/IE の DOM は位置やサイズに関する限り スクリプトから殆ど利用価値がないような気がします。 f(^^;

因みに、IE のこれらの値の詳細な情報は、多分(笑)、MS のこのページに書いてあります。

Internet Explorer 6 における CSS の拡張
Measuring Element Dimension and Location

一応確認のための HTML はこちら

サンプル1 IE6 旧仕様モード
サンプル2 IE6 W3C 標準準拠モード


タイトなレイアの生成

レイアを Layerコンストラクタや insertAdjacentHTMLメソッドなどでダイナミックに生成する場合、 タイトなレイアが作りたいときがあります。

レイアの内容がサイズの分かる画像や固定サイズの TABLE の場合は問題ありませんが、 文字列を含む場合は少々面倒です。

それは以下の理由によります。

まあ、ブラウザ毎に対処すれば良いのですが、それではメンドーです。

この Tip集的解決法は... まあ、こんな風にしてみるのも手かと(笑)。

// HTML な場合
<head>
    :
<style type="text/css"><!--
.t{ position:absolute; }
--></style>
<script language="JavaScript"><!--
var s='';
if(document.all && !document.getElementById){ // (*1)
  s+='.t{ width:1px; }\n';
}
if(s!='')
  document.write(
       '<style type="text/css">\n'
      +s+'<\/style>\n');
// --></script>
</head>
<body>
    :
<div class="t"><table><tr><td nowrap>
書き出したい文字列など
改行などは必要に応じて自分で書くっ
</td></tr></table></div>
    :

*1 確か、IE5.0 の初期バージョンではこれに絡んだバグがあったと思うけど、 昨今の相次ぐセキュリティホールのバグフィックスをあてると 治っちゃう(?)ような気がするので無視っ。 f(^^;

// JavaScript な場合
var div=createLayer(x,y,0,0,null,
           '<table><tr><td nowrap>'
          +'書き出したい文字列など'
          +'改行などは必要に応じて自分で書くっ'
          +'<\/td><\/tr><\/table>'); // (*2)

*2 createLayer 関数は「ライブラリ集」で紹介している関数で、 実は既にこの関数内で対処していたりします。

だから、詳細はそちらを見てね〜


Number オブジェクト

Number オブジェクトは ECMA-262 で IEEE準拠の 64bit Floating Arithmetic であると定義しています。

例えば、NN4 のレイア にある moveBy メソッドを次のように定義した場合 結果は同じだと思います?

div.left            += dx; // NN4
div.style.pixelLeft += dx; // IE4

答えは「No」

NN4 では結果は実数演算した値ですが、IE4 では整数になるようです。

つまり、レイアを距離L だけ移動するのに F回の操作で移動するとすれば、 1回あたり L/F の移動量になりますが、この計算結果をそのまま上の式に当てはめると IE4 では誤差が生じて目的の位置にはならないケースが出るということになります。

このような場合の回避策は色々と考えられますが、 最も簡単な手法は差分で移動するのではなく、絶対位置を計算するようにすることでしょうか。

// 毎回絶対座標を計算する
div.style.pixelLeft = Math.floor(x0+i*L/F);
// または絶対座標を表すプロパティを追加して絶対位置を計算する
div.currentLeft+=dx;
div.style.pixelLeft = div.currentLeft;

背景色の変更

一度設定した背景色を変更するには bgColor/backgroundColor 属性を変更すれば良いのですが、 NN4 ではレイアの背景色を意味するプロパティ "bgColor" はスタイル上では "layer-background-color" であり、"background-color" に相当する属性はないようです。

これは div.bgColor = '#339966'; と指定しても レイア内にある文字などの背景色までは変更されないことを意味します。

文字部分の背景色を設定するためにはもう一度レイア内に HTML を書き出せば良いのですが、 div.document.write(html); を実行すると、 今度はレイアに指定してあるスタイルは解除されてしまうようです(困)。

従って、NN4 で一度設定した背景色を変更する場合にはレイアにスタイルを指定するのではなく レイア内の HTML にスタイルを指定したブロックタグでラップしたものを div.document.write(html); する必要があるようです。

// サンプル
    :
<!-- スタイルの定義 -->
<style type="text/css"><!--
.t { position:absolute; }
.s { font:normal 18pt sans; }
--></style>

// スクリプト
<script language="JavaScript"><!--
html = '<div class="s">\n'
     + 'NN4 で文字などを含むレイアの<br>\n'
     + '背景色を変更するには苦労する\n'
     + '<\/div>';
function changeColor(){
  var div=getDivFromName('t1');
  setDivBackgroundColor(div,'#339966');
  writeDivHTML(div,true,true,html);
}
// --></script>
    :
// レイアの定義
<div class="t" id="t1"><div class="s">
NN4 で文字などを含むレイアの<br>
背景色を設定するには苦労する
</div></div>

*1 スクリプト内で使用している関数は「ライブラリ集」で紹介している関数です

この例は予めレイアを HTML で定義していますが、コンストラクタで生成した場合は 最初っからスタイルは指定できませんがね(笑)


オブジェクトと prototype プロパティ

prototype 構文はクラスのプロパティ定義を行うために使用しますが、 ブラウザが提供しているオブジェクトに対して定義する場合には ちょっとした注意が必要です。

*1 document.write(document.all.div.constructor);

と記述すれば 'undefined' になります。


見えないレイア

IE でレイア内部の書換え・移動など大量に行うと表示更新が抜ける場合があるようです (*1)

これは、「DHTML のやってはいけないこと」で紹介している 「スクロールに合わせてレイアを移動」と同じ現象と思われますが、 表示更新抜けのメカニズムが想像できないので回避策もとれません。

この現象が発生する場合、レイアの visibility を再設定してやると見える場合もありますが、 必ず見えるようになるわけでもなさそうです。

*1 確認したのは IE4 ですが、その後のバージョンでも発生するような気がします。

また、IE6 あたりでは1度に大量にレイアを生成すると表示座標もずれる場合がありそうです。

困ったものだ (-_-;

一応、回避策としては次のような手を打ってみるとなんとかなりそうです。

  1. 対象となるレイアの visibility を一旦 'hidden' に設定する
  2. レイア内部の書換えや移動など必要な処理を行う
  3. setTimeout により一定時間( 例えば 500msec )待ち、 それから visibility を一括して 'inherit'( または visible) にする

レイアの表示制御手法

よく「レイアの表示/非表示は visibility制御するより見えない位置に移動した方が速い」 と言いますが、本当はどうでしょうか?

これは難しい問題です。 何故なら表示スピードは見た目の体感スピードで測る必要があるからです。

つまり、マルチプロセスやスレッド制御を行っている場合には 関数やプロパティ変更の処理スピードが速いから表示スピードも速い とは言えない場合があるからです。

また、当然プラットフォーム( OS )の違いでも特性は変わるでしょう。

しかし、IE, NN 2つのブラウザ共に制御構造は似ていて、 処理スピードに依存すると仮定して(実際は違うかもしれません)、 Windows用 IE,NN で処理スピードを測定してみました。

結果は以下の比率になりました。

ブラウザ種類タイル入れ子(子)入れ子(親)重ね
NN4 visibility 1.00 1.05 2.20 1.05
移動  1.32 1.32 2.09 1.26
IE4 visibility 6.20 7.14 7.31 6.37
移動  9.2811.2 12.1  9.18
IE5.5 visibility 3.74 3.5718.5  3.73
移動 87.4 85.7 90.2 85.1 
IE6 visibility 4.55 4.5522.7  4.56
移動 89.6 90.3 93.8 89.4 

*1 このデータはこのスクリプトを 使用して測定したもので、 最も時間が短い NN4 の visibility制御を 1.00 とした処理時間の比率です。

測定は visibility の inherit/hidden のセット 及び (-1000,-1000)/(1000,1000) の相対移動のセットを各 10,000回実施した処理時間から ループ処理にかかる処理時間を引いたもので比較しています。

測定結果から visibility制御が移動より遅い場合は NN4 で子レイアを持つ複雑なレイアの表示制御の場合だけで、 実質的には差が殆どありません。

また、IE の測定結果では明らかに visibility制御を行った方が効率てきです。 しかも、IE はバージョンを重ねる毎にスピードが改善されたと言う割には これらの処理に関しては次第に悪くなっています。

そこで、1関数内で繰り返し処理をせず setTimeout を使用して1回の変更毎に 一旦ブラウザに制御を渡すようにして測定してみると各操作毎に表示更新される ようになり、繰り返し処理自体は当然速くはないのですが、レイアの構成に依存せず ほぼ同じ処理時間となりました。

*2 このことから、ひょっとすると各属性の変更に伴う処理時間より 描画のためのバッファリング処理に負担が大きいことを示しているのかもしれません

と言っても、ほとんど誤差の範囲内なので全く信用はできませんが(笑)... f(^^;

結論的には、レイアの表示/非表示をする場合には visibility を変更した方が得策で、 しかも1度にたくさんのレイア制御をする場合は1度に全ての変更をするより、 途中でブラウザに制御を渡しながら行う方が良いと思われます


write メソッドの実行速度

DHTML をやる上でレイア内の HTML を書き直すスクリプトを書くことがあります。

NN4 でこの種の処理を行うとディスクアクセスが発生しているのがわかります。
これは document.open()/write()/close() の一連の処理を行うと 一時的にファイルに書き出した後、 出力結果をまとめて評価する仕組みが働いているように見えます。

IE ではこの種の現象は見られません。

結局のところ、この種の処理を数秒間隔で繰り返す処理は避けた方が良いでしょう。


write メソッドとスタイル定義

レイア内のドキュメントを動的に変更する場合は NN4 では document.write メソッドを使用します。

しかし、特定のサイズのレイアにブラウザ依存の文字を書くのは問題があるので、 思わずスタイルシートで font-size を記述したくなります(笑)。

NN4 ではこのようなケースでスタイルシートを使用するとうまく動作しない場合があります。

◎出力対象のレイアのスタイルに定義する
反映されません。 BODYの定義になります。
◎class,id等を使用して定義する
スタイル本体で定義している限り反映されるようです。
◎インラインスタイルで定義する
初回は反映されますが2回目以降の出力は最後に出力したレイアの document に対してのみ有効なようです。

*1 この方法で1度でも出力すると class,id を使用しても反映されなくなります。

また、class または id で指定したスタイルとインラインスタイルを組み合わせると ほとんど write メソッドで書き込んだスタイル指定は効かなくなるようですので 注意して下さい。

もし、あなたのブラウザが NN4 なら ここで確認することができます。


タイマ管理の不思議

どうも変だヘンだと思っていたけど、やっぱりヘンだった。

え?何が? って、決まっているじゃないですか、 IE(Win版 IE)のタイマ管理です。 f(^^;

どうやら複数のウィンドウで setTimeout を同時に使用していると タイマが加速するみたいなんです。

実は性能測定のためのスクリプトを書いていて setTimeout の測定をしようとして インターバル値を "0" にしているウィンドウを2つ立ち上げたら動作が加速した(笑)。

*1 setTimeout(0) で実行しているのに更に動作が加速するってどーゆーこと?
M$社流のサボりテクですかね〜 f(^^;

IE の動作が鈍いと思ったときは2つ開くと速くなったりして...

更に面白いことに NN4 と同時に開くと IE, NN 共に速くなります。
ただ、NN4単独ではこのような現象にはなりません。

色々と考えてみると、どうもタイマ管理で Windowsメッセージを使用していて、 このメッセージが複数箇所で発生するとは考えていない作りの結果のようなって、 カンジー f(^^;


setTimeout と setInterval メソッド

通常、周期的な処理は setTimeout メソッドを使用しますが、 JavaScript1.2 からは setInterval メソッドが実装されました。

一般的には setTimeout方式が特定の時間間隔を指定するに対して インターバルタイマ方式は特定の処理の周期時間を指定します。

これらは似ていますが、本質的に異なるものです。
setTimeout方式では処理が呼び出される時間間隔は

周期時間+処理時間( オーバヘッドを含む )

であり、インターバルタイマ方式では

周期時間

となります。

つまり、setTimeout方式は自分の負荷状況に応じた時間尺度を用い、 インターバルタイマ方式は絶対的な時間尺度に従います。

特にインターバルタイマ方式では負荷状況により

周期時間<処理時間

となる状況が発生した場合の動作が問題となります。

そこで、NN/IE ではどのようになるかに興味が出てきます。
予想では NN は次回の処理の呼び出し時間が詰まる( キュー方式 )のに対して、 IE では呼び出し回数が減る、というものでした。

この予想は NN に関しては当たりましたが、IE の処理は全く驚くべきもの(笑)でした。 結果としては setInterval メソッドはいわゆるインターバルタイマ方式ではなく、 単に setTimeout の簡易関数のように以下の周期で呼び出されるものでした。

周期時間+処理時間( オーバヘッドを含む )

この動作はここで 確認することができます。

** IE はこーいったところがザツなんだよなぁ〜 (-_-;


mouseover と mouseout

IE と NN のイベントハンドリングの仕組みは全く異なります。

あるレイアに対して onmouseover, onmouseout イベントを登録した場合、 NN では指定したレイアに対するイベントとして通知されますが、 IE では指定したレイア内の最上位に表示されている構成要素に対するイベントとして通知されます。

例えば、下のレイアでイベントハンドリングを行う場合を考えてみると良くわかります

親レイア
  子レイア1
リンク
子レイア2
リンク
 

IE の場合、どのレイアに登録したイベントからの通知かを window.event オブジェクトから 直接知ることはできません。

このために、通常は srcElement プロパティから属する親のレイアを探索し、 判断しなければなりません。

しかし、同じレイアに属する要素が異なってもイベントが発生しますから srcElement では本当に mouseover/mouseout が発生したかは判断できません。

登録したレイア上で本当に mouseover/mouseout が発生したかどうかを判定するためには fromElement と toElement から判断します。

しかし、fromElement や toElemenet のどちらか一方は登録したオブジェクトの要素ですが、 他方はなんらかのオブジェクトが設定されている保証はありません。

これは NN と IE の DOM構造 及び DOMの管理体系が異なることが原因と思われます。

このため、本来スクリプト側の独立性を保つのに必要な機能がエンジン側にない、 いわば仕様設計上の欠陥のように思えます。


mousedown と mousemove

ドラッグ処理などを作成する場合、 mosedown,mousemove イベントを使用することになります。

これらのイベントは色々なオブジェクトに対して定義することができます。

しかし、これらのイベントを特定のオブジェクト(DIV要素)に登録した場合、 IE と NN4 では同じ動作をするでしょうか?

.

答えは両ブラウザで上のレイアをマウス操作してみればわかります。

単にレイア上でマウスを移動するのみ
IE,NN 共に同じようにレイア上にマウスがあるときのみ onmousemove が呼び出されます。
マウスボタンをレイア上で押してドラッグ操作する場合
IE では単にレイア上でマウスを移動した時と同じ動作になりますが、 NN4 ではマウスを占有するのでレイア外にマウスが出ても onmousemove が呼び出されます。

IE では onmousemove や onmouseup をドラッグ操作に使用するために レイアなど対象のオブジェクトにイベントハンドラを登録すると、 素早くマウス操作をするとドラッグできない状況が発生したり、 マウスを離しているのにドラッグ中の状態のまま残ることになります。

つまり、IE ではドラッグ処理をする場合は対象のオブジェクトではなく body,document に登録し、 マウスの占有処理をスクリプト側で行わなければならないことを意味します。

注 ウィンドウ外へのドラッグを考えれば、これでも完全にはできません

ブラウザの仕様を言っても始まらないかもしれませんが、 やはり IE のオブジェクトモデルは手抜きとしか思えませんねぇ (-_-;

注 NN4, IE4 の場合レイア上で mousemove,mouseup をハンドリングしていると マウス移動していないのもかかわらず、マウスを離した際に mouseup イベントの後に mousemove イベントが呼び出されるようです。
#> IE6 ではこの問題は修正されているようです


mousedown,mouseup そして click

onmousedown,onmouseup と似たイベントに onclick があります。
これらのイベントは少なくとも単独では問題なく動作します。

例えば Windows ユーザであれば付箋紙ソフトを思い浮かべてください。
このソフトは表示されているウィンドウをマウスドラッグすれば移動し、 クリックすれば文字列入力になります。

これと同じ動作を onmousedown,onmouseup,onclick を使用して実現できるでしょうか?

答えは「難しい」です(笑)

簡単な NN4 から言うと onclick を指定し caputre しても、 通知されるのは指定した document と document上にある link部分で行った click操作のみです。
その他の document上にある文字列や画像上で click 操作をしても onclick で指定した関数には通知されません。

回避策としてはレイアと同一サイズの透明のレイアを配置し、 透明なレイア上で clickイベントのハンドリングをするくらいでしょうか。

IE の場合はもう少し複雑です。
つまり、NN4 ではドラッグ操作では onclick は呼び出されませんが、 IE ではドラッグ操作をしても onclick が呼び出されます。
つまり、onmousemove イベントで監視しないとドラッグ操作後の onclick かどうか 判定できません。 また、NN4 では onmousedown,onmouseup の戻り値で onclickイベントの発生を 抑止することができますが IE4 ではできません。

回避策としては onmousedown,move の各イベントでドラッグ操作を行ったかどうかを スクリプト側で監視し、 onclickイベント発生時にこれをチェックすることくらいでしょうか。


onmousemove と document.write

特定のレイアに対して onmousedown,onmousemove,onmouseup を登録する場合 NN4 ではレイア内の document に対して行います。

もし、これらのイベントを登録したレイアのドキュメントを書き替えるとどうなるでしょうか?

可能性としては次の2つが想定できます。

  1. 全てのイベントの登録が破棄される
  2. 特に何も変わらない

しかし想像とは異なって、NN4 では実際には onmousemove イベントのみ破棄されます(*1)

これを回避するには以下の方法が考えられます

  1. document.write の後に再度 onmousemove イベントを登録する。
  2. document.write を使用するレイアには onmousemove イベントを登録しない。
  3. レイアの document に登録することをやめ、 window.document に対して登録し、 後は全て自分で処理する。

*1 IE は破棄されません。どうも、ワザとのような気がしますが理由は不明です


Copyright(c) 1998 - 2001 ShinSoft. All rights reserved.