Thomas Fuchs「javascriptが原因で再フローとレンダリングが発生するケース」
mir.aculo.us 2010.8.17のブログエントリ
When does JavaScript trigger reflows and rendering?
以下斜め読んだ内容
- ブラウザはシングルスレッドなので飼いならしやすい方
- chromeはタブごとに別スレッドになるが各タブ内はシングルスレッド
- おっちょこちょいなjs書いてるとそれが原因で再フローやレンダリングが余計発生する場合があり、それは結構ダメージになる
- 鉄則
- htmlの再フロー・レンダリングはブラウザで一番高くつく処理
- ページがカクカク・もっさりしてる?
- レンダリングに問題がある可能性大
- よくある最適化
- DOMツリーをバッサリ刈り取ってスッキリさせる
- CSSのスタイルをシンプルにする
- html/cssにこれといった問題なくてもjsに問題があることがある
- 再フロー・レンダリングが高くつく処理といわれてもピンと来ない人
http://video.google.com/videoplay?docid=-5863446593724321515&hl=en
- js使ってcssスタイルを修正してページの表示を変えるときに起こること
- 次のいずれかが発生することを待ってからブラウザはスタイル変更を反映させるため「待ち時間」発生
- 1:jsの実行の終了
- 例えばイベントハンドラにセットされた関数の実行の終了
- この場合、待ち時間短縮のためにできることはゼロ
- 2:再フロー発生の引き金となるような処理の(ブラウザへの)リクエスト
- この場合は最適化可能。うっかり引き金を引いている場合があるから。
- 1:jsの実行の終了
- 次のいずれかが発生することを待ってからブラウザはスタイル変更を反映させるため「待ち時間」発生
- コード例で説明
//コード例:before //ページ内のある要素(ex.div#header)への処理 //フォントサイズを14pxに変更してみて、 //この要素の高さが100px以上だったら特定の処理を実行する someElement.style.fontSize = "14px"; if(someElement.offsetHeight>100){ /* ... */ }//ここで再フロー発生 //この要素の左パディングを20pxに変更してみて、 //この要素の横幅が100px以上だったら特定の処理を実行 someElement.style.paddingLeft = "20px"; if(someElement.offsetWidth>100){ /* ... */ }//ここで再フロー発生
- offsetHeight/offsetWidthプロパティへのアクセス。これが再フローの引き金になってる
- offsetHeightへアクセス時に1回、offsetWidthへアクセス時に1回
- この点を押さえて書き直す
//コード例:after //先にfont-sizeとpadding-leftを変更してから、 //offsetHeight/offsetWidthプロパティへアクセスするやり方に変更 someElement.style.fontSize = "14px"; someElement.style.paddingLeft = "20px"; if(someElement.offsetHeight>100){ /* ... */ }//ここで再フロー発生 if(someElement.offsetWidth>100){ /* ... */ }//再フロー発生しない
- 書き直すと、再フロー回数は1回に減る。
- だいたいだけど速さは2倍になる
- 再フローが1回に減ったわけ
- 再フローの引き金はスタイル変更以外にも色々ある。
- DOMノードの追加/削除も再フローの引き金になる
- スタイル変更/ノードの追加・削除といった処理を(五月雨式ではなく)まとめて実行されるように、ページ上で実行したいjsのコードの最後の方に置く。
感想
- js経由でのcss変更はまとめて反映される
- これがこのエントリのポイントになる情報なんだけど、実際に各ブラウザがこういう動きしてるのかについてのソースをこれから探す