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

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







関連記事

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

同じラベルの記事を読み込み中...
Related Posts Plugin for WordPress, Blogger...