以下斜め読んだ内容

pseudo translation of useful posts, book reviews, remarks,etc. twitter: feeddict

Travis Leithead「IEで出る"Operation Aborted"ダイアログの原因および対策」(IEBlog)

ieblog 2008.4.23のエントリ
id:uupaaさんのtweet経由で知った記事

What Happened to Operation Aborted?

  • IE使ってるときに出るエラーダイアログ"Operation Aborted"を解説してるエントリ
  • これが出るとページ内容が全部破棄されるので怖い
  • IE7以前でエラーダイアログが出る条件、IE8での対処と、開発者向けの回避方法がまとまったエントリ
  • 「body要素内にscript要素は禁止」の論拠にもなるお話
以下斜め読んだ内容
  • "Operation Aborted"ダイアログが出ると
    • 今見てるページが見れなくなる
    • ダイアログ出る直前に表示されてなる内容は破棄される
    • ダイアログをクリックするまでは表示される内容は破棄されないので悪あがきする人もいる
  • IE8beta1ではこの怖い挙動はストップ
    • "Operation Aborted"ダイアログが出なくなった
      • このダイアログの副作用である表示内容全破棄もなくなった
  • "Operation Aborted"ダイアログには発生条件あり
  • htmlパース中に以下の条件をすべて満たすと"Operation Aborted"
    • 1.htmlファイルのパース処理が実行中
    • 2.同時にスクリプトが実行中
    • 3.実行されてるスクリプトがノードの追加・削除をやってる
    • 4.パースが完了していない(=終了タグまでパース完了してない)要素の子(孫)要素へのノード追加・削除である
      • 例外あり。script要素自身の親要素へのノード追加や親要素自身の削除のときは、"Operation Aborted"が出ない
//operation abortedが出るhtmlソース例
//body要素の子要素としての挿入
//div要素への挿入ならoperation abortedは出ない
<html>  
<body>  
  <div> 
   <script type="text/javascript"> 
var newElem = document.createElement('foo'); 
    document.body.appendChild(newElem); 
   </script> 
  </div> 
</body> 
</html>
  • iframe要素の中でも"Operation Aborted"が発生すると、モーダルのダイアログが表示され表示内容が破棄される
  • jsを使ったiframe要素のが"Operation Aborted"が出やすい場所の1つになってる
  • IE8beta1で開発チームがやったことは
    • "Operation Aborted"ダイアログはモーダルで、かつ表示/非表示が設定で選べない
    • 単に"Operation Aborted"ダイアログを非表示するだけだとエラー発生してるページへユーザの訪問を許してしまう。
    • IE8では、htmlパーサーは(単にエラー通知するのでなく)"Operation Aborted"という例外を投げるときこうなる
      • 例外発生したことをスクリプトエラーが表示される場所で確認できるようにした
      • 例外発生時点でパース処理とスクリプト実行を停止させる
      • デバッグに役立つ情報をエラー表示に追加。KB記事番号を追加
//例外発生時のメッセージ例
//末尾にKB記事番号
HTML Parsing Error: Unable to modify the parent container element before the child element is closed (KB927917)
  • KB番号便利
  • 各ブラウザがいろいろな状況でパース処理をどうやってるのかを追ってみると各ブラウザで色々で面白い
    • オンザフライでのノード追加はどのブラウザでも大体同じ
      • appendChild使ったりするやつ
      • 単にノードを追加
      • メモリ上にあるDOMツリーへノードが追加される
    • オンザフライでのノード削除。パース処理の再開。この点はブラウザで対応が違ってくる
      • removeChild, innerHTML, outerHTMLとか使うやつ
      • パース再開時のコンテクストを何にするかでブラウザで違いが出てくる
      • パース完了していないノードの削除
      • 主流は一括削除
        • エントリ作者はこっちの動作に賛成
        • 削除されたノードのパースが完了(=終了タグのパースまで完了)するまで削除ノードの子孫要素のパースをスキップ
        • 削除ノード子要素・孫要素は残らずDOMツリーから削除。
        • well-formedなマークアップじゃない(=終了タグ抜けの多い)htmlの場合とパーサー側の処理が大変になる
      • スクリプト実行された時点でのノード全体を削除するブラウザも少数派だがある
        • script要素を子(孫)ノードとするノードの削除をするとき実行時点で対象ノードとパース完了してる子孫ノードだけを削除
        • script要素の弟要素などがある場合はスクリプト実行後にパースされDOMツリーから削除されない
        • パースとノード削除の結果、この弟要素は一世代上の要素としてDOMツリーに追加される
//ノード削除でブラウザによって結果が変わるケース
//script要素の親ノードを削除。
//script要素の兄弟要素は残すか、親ノードと一括して削除するか。
//エントリ作者は「一括削除」派
<html> 
<body> 
  <div id="container1"> 
   <div> 
    <span id="span1">First block of text</span> 
    <script type="text/javascript"> 
     document.body.removeChild(document.getElementById('container1')); 
    </script> 
    <span id="span2">Second block of text</span> 
   </div> 
  </div> 
  <div id="container2"></div> 
</body> 
</html>
//一括削除だと、body要素の子孫要素はdiv#container2のみ。
//別路線だと、body要素はspan#span2とdiv#container2の兄弟要素2つを子要素にもつ
  • IE7以前ではどうする?
    • マークアップ、script要素の位置や内容を調整できるときは"Operation Aborted"を回避できる
    • 自由に調整できないときもある。広告挿入など
    • 回避方法についてまとめあり
    • 回避方法のダイジェスト
      • scriptの実行タイミングをloadイベント発火後などに遅らせる
      • script要素にdefer属性追加する
        • おさらい。defer属性使うと、パース完了までscript要素内のコードの実行遅延が可能
      • DOMツリーの操作(=要素の追加・削除)の対象をscript要素の親要素に限定する
      • script要素をbody要素の子要素としてのみ配置する