追記:
タイトルが長かったため
「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 コードを設置し、ページの読み込み速度の計測を行いたいと思います。



コメントを投稿
コメント投稿機能について