日々のコンピュータ情報の集積と整理

Dr.ウーパのコンピュータ備忘録

2014年4月26日土曜日

ページ表示速度改善:SyntaxHighlighter使用箇所があれば読み込む(Bloggerに設置編)

追記:

タイトルが長かったため

「SyntaxHighlighter:ページの表示速度を改善するため、ページ内にSyntaxHighlighterを使用する箇所がある場合に、jsとcssを読み込むようにした」



「ページ表示速度改善:SyntaxHighlighter使用箇所があれば読み込む」

に修正しました。

はじめに

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 コードを設置し、ページの読み込み速度の計測を行いたいと思います。







関連記事

関連記事を読み込み中...

同じラベルの記事を読み込み中...