風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

Google ChromeにてDOMノード追加に異様に時間がかかる現象が発生して悩む→拡張機能が原因だった

JavaScriptでページ遷移用のナビゲーションを作っていたのだが、リンクと半角スペース(テキストノード)とを交互に挿入するような実装にしていると、なぜか異様に時間がかかってしまう(数百個程度のノード挿入に、数秒要する)。
どうやら、ナビゲーションのノードがDOMツリー上にあるとこうなるようで、いったんDOMツリーから切り離した状態で作成し、最後にDOMツリーに戻すようにすると、ほとんど時間はかからない。でもなぜ……?


さんざん悩んだが、結局はとある拡張機能が原因だった、というオチ(今回の場合ははちまバスター)。
もっと早くに、シークレットウィンドウで調べておくべきだった……。
【追記】なお、はちまバスターは早々に改修された

テスト

function test_add_nodes( options ) {
    if ( ! options ) {
        options = {}
    }
    var parent_node = ( options.parent_node ) ? options.parent_node : document.body,
        before_processing = !! ( options.before_processing ),
        node_number = ( 0 < options.node_number ) ? options.node_number : 300,
        exclude_link = !! ( options.exclude_link ),
        exclude_text = !! ( options.exclude_text );
    
    var container = document.createElement('div'),
        link_node_template = document.createElement('a');
        text_node_template = document.createTextNode('');
        before_time = new Date().getTime();
    
    if ( parent_node && before_processing ) {
        parent_node.insertBefore( container, parent_node.firstChild );
    }
    for ( var ci=0; ci < node_number; ci++ ) {
        if ( ! exclude_link ) {
            var link_node = link_node_template.cloneNode( true );
            link_node.href = '#';
            link_node.innerHTML = ci;
            container.appendChild( link_node );
        }
        if ( ! exclude_text ) {
            var text_node = text_node_template.cloneNode( true );
            text_node.textContent = '*';
            container.appendChild( text_node );
        }
    }
    if ( parent_node && ( ! before_processing ) ) {
        parent_node.insertBefore( container, parent_node.firstChild );
    }
    var after_time = new Date().getTime(),
        elapsed_time = ( after_time - before_time );
    
    return elapsed_time;
}

function log_result( test_name, elapsed_time ) {
	console.log( test_name + ' : ' + ( elapsed_time / 1000.0) + ' sec' );
}

// DIV要素にリンクやテキストノードを一通り追加してから、DOMツリーに挿入
log_result( 'after , text only', test_add_nodes( { before_processing : false, exclude_link : true, exclude_text : false } ) );
log_result( 'after , link only', test_add_nodes( { before_processing : false, exclude_link : false, exclude_text : true } ) );
log_result( 'after , both     ', test_add_nodes( { before_processing : false, exclude_link : false, exclude_text : false } ) );

// DIV要素をDOMツリーに予め挿入してから、リンクやテキストノードを追加
log_result( 'before, text only', test_add_nodes( { before_processing : true, exclude_link : true, exclude_text : false } ) );
log_result( 'before, link only', test_add_nodes( { before_processing : true, exclude_link : false, exclude_text : true } ) );
log_result( 'before, both     ', test_add_nodes( { before_processing : true, exclude_link : false, exclude_text : false } ) );
拡張機能有効時
after , text only : 0.003 sec
after , link only : 0.057 sec
after , both      : 0.054 sec
before, text only : 0.04 sec
before, link only : 0.04 sec
before, both      : 6.856 sec // これは酷い
拡張機能無効時
after , text only : 0.002 sec
after , link only : 0.005 sec
after , both      : 0.01 sec
before, text only : 0.027 sec
before, link only : 0.045 sec
before, both      : 0.05 sec