追記:
タイトルが長かったため
「SyntaxHighlighter:ページの表示速度を改善するため、ページ内にSyntaxHighlighterを使用する箇所がある場合に、jsとcssを読み込むようにした」
を
「ページ表示速度改善:SyntaxHighlighter使用箇所があれば読み込む」
に修正しました。
SyntaxHighlighterを使用するとページに記載したソースコードをきれいに表示することができます。タイトルが長かったため
「SyntaxHighlighter:ページの表示速度を改善するため、ページ内にSyntaxHighlighterを使用する箇所がある場合に、jsとcssを読み込むようにした」
を
「ページ表示速度改善:SyntaxHighlighter使用箇所があれば読み込む」
に修正しました。
はじめに
しかし、SyntaxHighlighterを利用する場合、(当然ですが)外部のJavaScriptとスタイルシートを読み込む必要があり、その分表示が遅くなります。
従って、SyntaxHighlighterを使用しているページならその表示遅延は許容せざるを得ませんが、SyntaxHighlighterを使用していないページまでその表示遅延を許容する必要はありません。
そこで、SyntaxHighlighterの使用状況に応じて、読み込むSyntaxHighlighterのJavaScriptとスタイルシートを動的に変更する仕組みを考えてみました。
今回は前回作成した SyntaxHighlighter の動的読み込み JavaScript を 少し修正し、Blogger に設置してみました。
Blogger で使用している SyntaxHighlighter の機能
私の Blogger で使用している SyntaxHighlighter の機能は以下のとおりです。<!-- SyntaxHighlighter START --> <link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/> <link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'> </script> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js' type='text/javascript'> </script> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js' type='text/javascript'> </script> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js' type='text/javascript'> </script> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'> </script> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'> </script> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js' type='text/javascript'> </script> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPlain.js' type='text/javascript'> </script> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushVb.js' type='text/javascript'> </script> <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'> </script> <script language='javascript' type='text/javascript'> SyntaxHighlighter.config.bloggerMode = true; SyntaxHighlighter.all(); </script> <!-- SyntaxHighlighter END -->
これらを読み込めるように、前回作成した JavaScript を修正します。
必要な SyntaxHighlighter の機能を読み込めるように修正した Blogger用のJavaScript
Blogger用のJavaScript と書いてますが、<script type="text/javascript"></script>のすぐ内側に//<![CDATA[ と //]]> を追加しただけです。それ以外の部分はそのまま普通の Web ページに設置できる形をとっています。
<script type="text/javascript">
//<![CDATA[
<!--
    (function () {
        // --- デバッグ用 Util -------------------------------------------
        var isDebug = true;         // デバッグフラグ
        function log(txt) {
            if (isDebug) {
                console.log(txt);
            }
        }
        (function () {
            // consoleが使えない場合は空のオブジェクトを設定しておく
            if (typeof console === "undefined") {
                console = {};
            }
            // console.@@がメソッドでない場合は空のメソッドを用意する
            if (typeof console.log !== "function") {
                console.log = function () { };
            }
        })();
        // --- Util -------------------------------------------
        // css動的挿入
        function addStyleSheet(href) {
            log("LoadMinimumSyntaxHighlighter, addStyleSheet, href=" + href);
            var link = document.createElement("link");
            link.setAttribute("rel", "stylesheet");
            link.setAttribute("type", "text/css");
            link.setAttribute("href", href);
            header_setChild(link);
        }
        // JavaScript動的挿入
        function addScript(src, sync) {
            log("LoadMinimumSyntaxHighlighter, addScript, src=" + src + ", sync=" + sync);
            var script = document.createElement('script');
            script.setAttribute("type", "text/javascript");
            script.setAttribute("src", src);
            // 同期的に読み込むように指定されていた場合、
            // スクリプトの終了を検出するコードを埋め込み
            if (sync) {
                script.onload = script.onreadystatechange = function () {
                    log("LoadMinimumSyntaxHighlighter, onload|onreadystatechange, script.readyState=" + script.readyState);
                    // onload イベント もしくは onreadystatechange イベントで 読み込みが完了状態 のいずれかだったら
                    // IE と その他ブラウザに対応するために onload と onreadystatechange の両方のイベントに対応している
                    if (!script.readyState || /loaded|complete/.test(script.readyState)) {
                        script.onload = script.onreadystatechange = null;
                        // 非同期メソッドの終了を通知する
                        runSync_NotifyAsyncMethodEnd()
                        log("LoadMinimumSyntaxHighlighter, ScriptLoaded, script.readyState=" + script.readyState);
                    }
                };
                // 非同期処理である JavaScript の動的挿入を同期的に実行する
                runSync_AsyncMethod(function () {
                    header_setChild(script);
                });
            } else {
                header_setChild(script);
            }
        }
        // <head>取得
        function getHeader() {
            return document.getElementsByTagName("head")[0];
        }
        // <head>に子要素を追加
        function header_setChild(child) {
            var head = getHeader();
            head.appendChild(child);
        }
        // 同期実行:即終了メソッド
        function runSync_SyncMethod(func) {
            runSync(function () {
                log("LoadMinimumSyntaxHighlighter, SyncMethod, Start");
                syncRunningFlag = true;
                func();
                syncRunningFlag = false;
                log("LoadMinimumSyntaxHighlighter, SyncMethod, End");
            });
        }
        // 同期実行:非同期メソッド
        function runSync_AsyncMethod(func) {
            runSync(function () {
                log("LoadMinimumSyntaxHighlighter, AsyncMethod, Start");
                syncRunningFlag = true;
                func();
                /* syncRunningFlag = false; は非同期メソッドの終了イベントに委譲 */
                log("LoadMinimumSyntaxHighlighter, AsyncMethod, TrigEnd");
            });
        }
        // 同期実行:非同期メソッドの終了を通知する
        function runSync_NotifyAsyncMethodEnd() {
            syncRunningFlag = false;
            // 同期的に実行するためのチェーン処理
            runSyncChain();
        }
        // 要素の同期的実行
        var syncFuncArray = new Array();        // 実行待ち配列(先頭から実行されていく)
        var syncRunningFlag = false;            // 同期実行中フラグ
        function runSync(func) {
            // 同期実行中かつ実行待ちメソッドがなければ即実行するが、
            // それ以外であれば、実行待ち状態にする
            if ((!syncRunningFlag) && (syncFuncArray.length == 0)) {
                log("LoadMinimumSyntaxHighlighter, runSync, NowRun");
                runSyncChainAfterRunFunc(func);
            } else {
                log("LoadMinimumSyntaxHighlighter, runSync, RunLater");
                syncFuncArray.push(function () {
                    runSyncChainAfterRunFunc(func);
                });
            }
        }
        // 同期処理の必要なメソッドを実行した後に、同期的に実行するためのチェーン処理を実施
        function runSyncChainAfterRunFunc(func) {
            func();
            runSyncChain();
        }
        // 同期的に実行するためのチェーン処理
        function runSyncChain() {
            log("LoadMinimumSyntaxHighlighter, runSyncChain, Start, syncRunningFlag=" + syncRunningFlag + ", syncFuncArray.length=" + syncFuncArray.length);
            // 処理未実行状態 かつ 実行待ちメソッドがある場合に実行待ちメソッドを実行
            if (!syncRunningFlag) {
                if (syncFuncArray.length > 0) {
                    log("LoadMinimumSyntaxHighlighter, runSyncChain, RunSyncChain, Start");
                    // 先頭の実行待ちメソッドを取り出し、実行待ちから削除した後実行
                    var func = syncFuncArray[0];
                    syncFuncArray.splice(0, 1);
                    func();
                    log("LoadMinimumSyntaxHighlighter, runSyncChain, RunSyncChain, End");
                }
            }
            log("LoadMinimumSyntaxHighlighter, runSyncChain, End, syncRunningFlag=" + syncRunningFlag + ", syncFuncArray.length=" + syncFuncArray.length);
        }
        // --- main -------------------------------------------
        LoadMinimumSyntaxHighlighter();
        // 最低限の SyntaxHighlighter を読み込む
        function LoadMinimumSyntaxHighlighter() {
            log("LoadMinimumSyntaxHighlighter, Start");
            var commonURL = "http://alexgorbatchev.com/pub/sh/current/";        // 共通 URL
            var scriptURL = commonURL + "scripts/";
            var cssURL = commonURL + "styles/";
            var brushURLs = new Array();                                                // 各ブラシ用 URL
            brushURLs["js"] = scriptURL + 'shBrushJScript.js';
            brushURLs["xml"] = scriptURL + 'shBrushXml.js';
            brushURLs["csharp"] = scriptURL + 'shBrushCSharp.js';
            brushURLs["cpp"] = scriptURL + 'shBrushCpp.js';
            brushURLs["css"] = scriptURL + 'shBrushCss.js';
            brushURLs["java"] = scriptURL + 'shBrushJava.js';
            brushURLs["perl"] = scriptURL + 'shBrushPerl.js';
            brushURLs["plain"] = scriptURL + 'shBrushPlain.js';
            brushURLs["vb"] = scriptURL + 'shBrushVb.js';
            /*
            <pre> を検索し、SyntaxHighlighter のブラシを検索する
            ブラシが見つかったら、ブラシに応じた js を読み込む
            初期発見時には共通の css と js を読み込む
            */
            var brushCount = 0;                         // 見つかったブラシの数
            var preTags = document.getElementsByTagName("pre");
            for (var i = 0; i < preTags.length; i++) {
                var target = /(brush:\s*)([^\s]+)/;     // ブラシを発見するための正規表現
                var found = preTags[i].className.match(target);
                if (found != null) {
                    // 初回発見時は共通データの読み込みを実施
                    if (brushCount == 0) {
                        log("LoadMinimumSyntaxHighlighter, LoadCommon, Start");
                        addStyleSheet(cssURL + 'shCore.css');
                        addStyleSheet(cssURL + 'shThemeDefault.css');
                        addScript(scriptURL + 'shCore.js', true);
                        log("LoadMinimumSyntaxHighlighter, LoadCommon, End");
                    }
                    // ブラシ名からスクリプトを読み込む
                    addScriptFromBrush(found[2]);
                    brushCount++;
                }
            }
            // ページ内にブラシが存在したら、SyntaxHighlighter の使用準備を実行
            if (brushCount > 0) {
                runSync_SyncMethod(function () {
                    log("LoadMinimumSyntaxHighlighter, SyntaxHighlighter, Init, Start");
                    SyntaxHighlighter.config.bloggerMode = true;
                    SyntaxHighlighter.all();
                    log("LoadMinimumSyntaxHighlighter, SyntaxHighlighter, Init, End");
                });
            }
            log("LoadMinimumSyntaxHighlighter, End");
            // 各ブラシに対応するスクリプトの読み込みを行う
            function addScriptFromBrush(brush) {
                switch (brush) {
                    case "js":
                    case "jscript":
                    case "javascript":
                        addScriptFirst("js", brushURLs);
                        break;
                    case "xml":
                    case "xhtml":
                    case "xslt":
                    case "html":
                    case "xhtml":
                        addScriptFirst("xml", brushURLs);
                        break;
                    case "csharp":
                    case "c-sharp":
                        addScriptFirst("csharp", brushURLs);
                        break;
                    case "cpp":
                    case "c":
                        addScriptFirst("cpp", brushURLs);
                        break;
                    case "css":
                        addScriptFirst("css", brushURLs);
                        break;
                    case "java":
                        addScriptFirst("java", brushURLs);
                        break;
                    case "perl":
                    case "pl":
                        addScriptFirst("perl", brushURLs);
                        break;
                    case "plain":
                    case "text":
                        addScriptFirst("plain", brushURLs);
                        break;
                    case "vb":
                    case "vbnet":
                        addScriptFirst("vb", brushURLs);
                        break;
                    default:
                        log("LoadMinimumSyntaxHighlighter, addScriptFromBrush, Brush Not Found");
                }
            }
            // 初回のみスクリプトの読み込みを実施
            function addScriptFirst(type, URLs) {
                if (URLs[type] != "") {
                    log("LoadMinimumSyntaxHighlighter, addScriptFirst, type=" + type + ", URLs[type]=" + URLs[type]);
                    addScript(URLs[type], true);
                    URLs[type] = "";
                }
            }
        }
    })();
//-->
//]]>
</script>
Blogger への設置
以前のコードは<head>内に設置していましたが、今回私が作成したソースコードはSyntaxHighlighterで処理対象の<pre>が全て読み込まれた段階で実行するように調整します。そこで、</body>のすぐ上に設置することにしました。
結果
表示・読み込まれたファイル
変更前
↓
変更後
変更前・変更後の変化からわかるように、変更後は変更前では読み込まれていたページで使用されていないSyntaxHighlighterの機能の JavaScript は読み込まれていません。
かつ、ページのソースコードに SyntaxHighlighter が適用され、ソースコードがきれいに表示されています。
Blogger への SyntaxHighlighter の動的必要最低限機能の読み込み JavaScript の設置は成功です!!
まとめ
Blogger への SyntaxHighlighter の動的必要最低限機能の読み込み JavaScript の設置は成功しました。今回設置した JavaScript コードは全く最適化されていないものです。
次回は 最適化した JavaScript コードを設置し、ページの読み込み速度の計測を行いたいと思います。



 
コメントを投稿 (ここをクリックしてコメント投稿フォームを表示)
コメント投稿機能について