dTree を Movable Type 4.1/MTOS のネイティブタグで実装する
Movable Type 4.1/MTOS で変数の算術演算子と配列およびハッシュが実装されたことで、PHPあるいはJavaScriptとの組み合わせが必要だった処理が、ネイティブなタグのみで実装可能になりました。
全てのコードについて移植が可能であるかどうかは不明ですが、どの程度の威力があるか検証してみたかったので、サンプルとして、以前紹介した dTree のカスタマイズについてテンプレートタグで実装してみました。
1.dTree について
機能や設定方法は下記の記事を参照願います(実際にご利用になる場合、この記事の4項までの設定が必要です)。
で、元記事で紹介したPHP版のスクリプトは下記の通りです(若干直しました)。
<script type="text/javascript">
d = new dTree('d');
d.config.useCookies=false;
d.add(0, -1,'Categories','javascript: void(0);');
<?php $cat_number = 0; $level = 0; $tree = array(); ?>
<MTTopLevelCategories>
<?php $tree[$level] = ++$cat_number; if(!$level) { ?>
d.add(<?php echo $cat_number ?>, 0,'<$MTCategoryLabel encode_php="1"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<?php } else { ?>
d.add(<?php echo $cat_number ?>, <?php echo $tree[$level - 1] ?>,'<$MTCategoryLabel encode_php="1"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<?php } $level++; ?>
<MTSubCatsRecurse max_depth="3">
<?php $level--; ?>
</MTTopLevelCategories>
document.write(d);
</script>
<p style="text-align:center"><a href="javascript: d.openAll();">open all</a> | <a href="javascript: d.closeAll();">close all</a></p>
再掲になりますが、dTree によるサブカテゴリーリストの完成イメージは次の通りです。
この表示を行うための JavaScript は次の内容になっています(上記のPHPスクリプトから生成されたものです)。
<script type="text/javascript">
d = new dTree('d');
d.config.useCookies=false;
d.add(0, -1,'Categories','javascript: void(0);');
d.add(1, 0,'test1','http://.../test/');
d.add(2, 1,'test1-1','http://.../test/test11/');
d.add(3, 2,'test1-1-1','http://.../test/test11/test111/');
d.add(4, 2,'test1-1-2','http://.../test/test11/test112/');
d.add(5, 1,'test1-2','http://.../test/test12/');
d.add(6, 5,'test1-2-1','http://.../test/test12/test121/');
d.add(7, 5,'test1-2-2','http://.../test/test12/test122/');
d.add(8, 0,'test2','http://.../test2/');
d.add(9, 8,'test2-1','http://.../test2/test21/');
document.write(d);
</script>
<p style="text-align:center"><a href="javascript: d.openAll();">open all</a> | <a href="javascript: d.closeAll();">close all</a></p>
スクリーンショットと JavaScript(のカテゴリー)の対応はなんとなくお分かりになると思いますが、
d.add(...)
の行で、カテゴリー部分を表示させています。
本エントリーでは、Movable Type のネイティブタグのみで、このカテゴリーを表示する部分の JavaScript を生成します。
2.dTree のカテゴリー表示部分のフォーマット
先程も簡単に紹介しましたが、JavaScript でカテゴリーを表示する部分は add 関数がキモとなっており、関数のパラメータは、
d.add([*1],[*2],[カテゴリー名],[カテゴリーのURL *3]);
- *1:カテゴリーに一意に割り当てられたdTree用のカテゴリー番号
- *2:そのカテゴリーが属する親カテゴリーの番号
"0"はdTreeのルートに属する場合(=最上位のカテゴリー) - *3:dTreeの生成するa要素のhref属性になります。
となっています。
上記の add 関数部分を、サブカテゴリーリストを出力するサブテンプレートを応用して作成します。以下、add 関数の各パラメータをどのようなロジックで作成するかという解説を交えながら、ネイティブなテンプレートを組み立てていきます。
3.各パラメータ用のデータ生成方法
3.1 第1パラメータ用のデータ生成
第1パラメータはカテゴリーに番号を順に付与していけば良いだけので、リスト1のように、
リスト1
<$mt:setvar name="cat_number" value="1"$>
<MTTopLevelCategories>
<$mt:setvar name="cat_number" op="++"$>
d.add(<$mt:getvar name="cat_number"$>,~略~);
<$MTSubCatsRecurse$>
</MTTopLevelCategories>
とすれば良いことが分かります。
1行目でカテゴリー番号保持用の変数(cat_number)を1で初期化し、MTTopLevelCategories タグのループ内でインクリメントしていきます。
そしてインクリメント後に add 関数の第1パラメータに表示させればOKです。
3.2 第2パラメータ用のデータ生成
第2パラメータの、自カテゴリーの属する親カテゴリー番号を算出する方法は色々なアプローチがあると思いますが、ここでの方法は、まず算出に必要なデータとして、現在処理されているカテゴリーの階層をインデックスとした自カテゴリー番号を、配列の変数として保持します。
「カテゴリーの階層」は、親カテゴリーの階層は0、子カテゴリーの階層は1、孫カテゴリーの階層は2...、という具合に、数値で表すこととします。
「カテゴリーの階層をインデックスとした自カテゴリー番号を、配列の変数として保持」とは、例えば一番最初の「カテゴリー1」を処理している時、このカテゴリーはルート階層(数値で表すと0)に属するので、
tree[0]=1
という変数を作成することを指しています。左辺の変数 tree のインデックス0が階層の番号を示し、右辺の1がカテゴリー番号を示します。
「カテゴリー2」を処理している時、このカテゴリーは親カテゴリー(数値で表すと1)に属するカテゴリーなので、
tree[1]=2
という変数を作成します。
この規則によって、各カテゴリーを処理する時には、刻々と内容が書き換わる、次のような配列が作成されます。
- カテゴリー1の処理中:tree[0]=1
- カテゴリー2の処理中:tree[1]=2
- カテゴリー3の処理中:tree[2]=3
- カテゴリー4の処理中:tree[2]=4
- カテゴリー5の処理中:tree[1]=5
- カテゴリー6の処理中:tree[2]=6
- カテゴリー7の処理中:tree[2]=7
- カテゴリー8の処理中:tree[0]=8
- カテゴリー9の処理中:tree[1]=9
少し話が難しくなりますが、このデータは次回の繰り返し処理で利用するためのものです。つまり、カテゴリー1の処理中に作成された、一番最初の、
tree[0]=1
は(MTTopLevelCategories の)繰り返し処理の中でそのまま保持されており、次のカテゴリー2の処理の時に利用される、ということだけをとりあえず覚えていてください。
前置きが長くなりましたが、上記のデータを作成するサブテンプレートは、リスト2のようになります(青色はリスト1からの追加部分)。
リスト2
<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
<$mt:setvar name="cat_number" op="++"$>
<$mt:setvar name="tree[$level]" value="$cat_number"$>
d.add(<$mt:getvar name="cat_number"$>,~略~);
<$MTSubCatsRecurse$>
<$mt:setvar name="level" op="++"$>
<$MTSubCatsRecurse$>
<$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>
2行目で配列インデックス用の変数levelを初期化し、5行目で、処理中のカテゴリー番号を、現在の階層をインデックスとした配列データに保持します。
階層の数値の増減は、MTSubCatsRecurse の直前と直後で操作しています。
プログラミングに不慣れな方には分かりにくいのですが、MTSubCatsRecurseはいわゆる再帰処理を行うためのタグで、現在処理しているカテゴリーに子カテゴリーが存在する場合、MTSubCatsRecurse の内容を取り出し、 MTTopLevelCategories の中で処理します。
つまり MTSubCatsRecurse は次の階層を処理するための再帰データとなるので、その直前で変数 level をインクリメントすることで、階層用のデータがインクリメントされます。
また、MTSubCatsRecurse の直後でデクリメントすれば、階層の数値を親カテゴリーの階層に戻せます。
さらに MTSubCatsRecurse の中に孫カテゴリーデータが存在すれば、変数 level はさらにインクリメントされることになります。
これでdTreeに必要なデータが揃いましたので、あとは子カテゴリーが属する親カテゴリーのカテゴリー番号を求めるだけです。
これは、現在処理されているデータの階層からデクリメントした配列の値、つまり自カテゴリーの上位の階層のカテゴリー番号を取得すればいい訳です。
言い換えると、例えば2行目の、
tree[1]=2
の親カテゴリー番号を求める場合、上位の階層は
tree[0]
であり、その中に親のカテゴリー番号が設定されているので、配列のインデックスをデクリメント(-1)、つまり、
tree[1-1]
とすれば、tree[0] が求まります。あとはそこに設定されているカテゴリー番号1を取得すれば良い訳です。
また、4行目の
tree[2]=4
の親カテゴリーを求める場合、上位の階層は
tree[1]
となり、その中にカテゴリー番号が設定されているので、インデックスをデクリメント(-1)、つまり
tree[2-1]
とすれば、tree[1] が求まるので、あとはそこに設定されているカテゴリー番号2を取得すれば良い訳です。
話をまとめると、リスト3のようになります(青色はリスト2からの追加部分)。
リスト3
<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
<$mt:setvar name="cat_number" op="++"$>
<$mt:setvar name="tree[$level]" value="$cat_number"$>
<mt:if name="level" eq="1">
d.add(<$mt:getvar name="cat_number"$>, 0,'<$MTCategoryLabel escape="js"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<mt:else>
d.add(<$mt:getvar name="cat_number"$>, <$mt:setvar name="tmp" value="$level"$><$mt:setvar name="tmp" op="--"$><$mt:getvar name="tree[$tmp]"$>,'<$MTCategoryLabel escape="js"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
</mt:if>
<$mt:setvar name="level" op="++"$>
<$MTSubCatsRecurse$>
<$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>
追加した6~10行目のうち、9行目が該当します。
第2パラメータの値に配列変数treeのデータを表示しています。
6行目の MTIf で処理を振り分けている理由ですが、親カテゴリーを処理している場合、さらに上位の階層(つまり dTree のルート階層)データを取得しようとしますが、これまでの処理では設定されていないデータなので、エラーを避けるため、その場合のみ、7行目で第2パラメータに固定で0を設定するようにしています。
3.3 第3パラメータ用のデータ生成
第3パラメータのカテゴリー名には、<$MTCategoryLabel escape="js"$>
を記述するだけです。なお、JavaScript として正常に動作するよう、escape モディファイアに "js" を指定します。
3.4 第4パラメータ用のデータ生成
第4パラメータには、リスト4のようにカテゴリーのリンク、
リスト4
<MTIfNonZero tag="MTCategoryCount">
<$MTCategoryArchiveLink$>
<MTElse>
javascript: void(0);
</MTElse>
</MTIfNonZero>
を設定します。
カテゴリーにブログ記事が1件以上投稿されている場合は、カテゴリーアーカイブへのリンクを設定し、投稿されていなければリンクの代わりに "javascript: void(0);" を設定し、リンクをクリックしてもページ遷移が発生しないようにしています。
最後に、第1パラメータと第2パラメータに表示される値をデクリメントします(青色はリスト3からの追加部分)。
リスト5
<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
<$mt:setvar name="cat_number" op="++"$>
<$mt:setvar name="tree[$level]" value="$cat_number"$>
<mt:if name="level" eq="1">
d.add(<$mt:getvar name="cat_number" op="--"$>, 0,'<$MTCategoryLabel escape="html"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<mt:else>
d.add(<$mt:getvar name="cat_number" op="--"$>, <$mt:setvar name="tmp" value="$level"$><$mt:setvar name="tmp" op="--"$><$mt:getvar name="tree[$tmp]" op="--"$>,'<$MTCategoryLabel escape="html"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
</mt:if>
<$mt:setvar name="level" op="++"$>
<$MTSubCatsRecurse$>
<$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>
これは変数cat_numberと変数levelの初期値を1にしているため、デクリメントしないと下のように、カテゴリー番号が2から開始されるためです。
d.add(2, 0,'test1','http://.../test/');
d.add(3, 2,'test1-1','http://.../test/test11/');
d.add(4, 3,'test1-1-1','http://.../test/test11/test111/');
d.add(5, 3,'test1-1-2','http://.../test/test11/test112/');
d.add(6, 2,'test1-2','http://.../test/test12/');
d.add(7, 6,'test1-2-1','http://.../test/test12/test121/');
d.add(8, 6,'test1-2-2','http://.../test/test12/test122/');
d.add(9, 0,'test2','http://.../test2/');
d.add(10, 9,'test2-1','http://.../test2/test21/');
変数cat_numberと変数levelの初期値を1にしているのは、初期値0の場合にリスト系のテンプレートタグで正常にインクリメントされない不具合を避けるためです。
これにdTree用の前後のスクリプトを加えれば完成です(青色はリスト5からの追加部分)。
リスト6
<script type="text/javascript">
d = new dTree('d');
d.config.useCookies=false;
d.add(0, -1,'Categories','javascript: void(0);');
<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
<$mt:setvar name="cat_number" op="++"$>
<$mt:setvar name="tree[$level]" value="$cat_number"$>
<mt:if name="level" eq="1">
d.add(<$mt:getvar name="cat_number" op="--"$>, 0,'<$MTCategoryLabel escape="html"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<mt:else>
d.add(<$mt:getvar name="cat_number" op="--"$>, <$mt:setvar name="tmp" value="$level"$><$mt:setvar name="tmp" op="--"$><$mt:getvar name="tree[$tmp]" op="--"$>,'<$MTCategoryLabel escape="html"$>','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
</mt:if>
<$mt:setvar name="level" op="++"$>
<$MTSubCatsRecurse$>
<$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>
document.write(d);
</script>
<p style="text-align:center"><a href="javascript: d.openAll();">open all</a> | <a href="javascript: d.closeAll();">close all</a></p>
よりエレガントな実装があると思いますが、その点はご容赦ください。
関連記事:dtree のサブカテゴリーリストにブログ記事タイトルを表示する for Movable Type
- Movable Type 4.2 のブログ記事ページを XHTML 1.0 Strict で valid にする
- Lightbox JS/Litebox で BlogPet を背景画像の下に隠す
- mt-site.js について(その3:cookie登録の改善)
- mt-site.js について(その2:注意事項)
- mt-site.js について(その1:仕組みと動作)
MTを始めたばかりで、こちらのサイトや書籍を参考にさせていただいております。
ブログの作成で大変助かっております。
1つご質問があるのですが、
今回紹介していただいているdTree によるサブカテゴリーリストに
各カテゴリ毎の記事数を表示したいのですが可能でしょうか。
この場合、<$MTCategoryCount$>は使えないのかなと思い悩んでおります。
できれば対応方法をご教授いただければ幸いです。
よろしくお願いします。
>backpackerさん
こんばんは。
ご返事遅くなりすいません。
ご質問の件ですが、下記で表示されます。
<script type="text/javascript">
d = new dTree('d');
d.config.useCookies=false;
d.add(0, -1,'Categories','javascript: void(0);');
<$mt:setvar name="cat_number" value="1"$>
<$mt:setvar name="level" value="1"$>
<MTTopLevelCategories>
<$mt:setvar name="cat_number" op="++"$>
<$mt:setvar name="tree[$level]" value="$cat_number"$>
<mt:if name="level" eq="1">
d.add(<$mt:getvar name="cat_number" op="--"$>, 0,'<$MTCategoryLabel escape="html"$> [<$MTCategoryCount$>]','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
<mt:else>
d.add(<$mt:getvar name="cat_number" op="--"$>, <$mt:setvar name="tmp" value="$level"$><$mt:setvar name="tmp" op="--"$><$mt:getvar name="tree[$tmp]" op="--"$>,'<$MTCategoryLabel escape="html"$> [<$MTCategoryCount$>]','<MTIfNonZero tag="MTCategoryCount"><$MTCategoryArchiveLink$><MTElse>javascript: void(0);</MTElse></MTIfNonZero>');
</mt:if>
<$mt:setvar name="level" op="++"$>
<$MTSubCatsRecurse$>
<$mt:setvar name="level" op="--"$>
</MTTopLevelCategories>
document.write(d);
</script>
<p style="text-align:center"><a href="javascript: d.openAll();">open all</a> | <a href="javascript: d.closeAll();">close all</a></p>
それではよろしくお願い致します。
お忙しいところコードまで教えていただきありがとうございます!
カテゴリ毎の記事数を表示することができました!
本当にありがとうございました。
>backpackerさん
こんにちは。
ご連絡ありがとうございました。
うまくできたようで良かったです。
ではでは!