検索のキーワードを強調表示する

mixiの「よってたかってMTテンプレ。」コミュニティで、「検索結果をGoogleっぽく強調表示させる方法」という問題が出題されました。
条件を限定すればMTタグだけで実現できますが、汎用的にするにはMTタグだけでは限界があります。

そこで、MTタグとJavaScriptを組み合わせて、検索のキーワードを強調表示する方法を考えてみました。

1.「検索結果」テンプレートにタグを追加

まず、「検索結果」のシステムテンプレートで、検索結果の一覧を表示する部分(「<MTSearchResults>」~「</MTSearchResults>」タグ)の直前に、以下を追加します。

<MTSetVarTemplate name="add_search_id"><MTIf name="search_id"> id="search_<MTGetVar name="search_id">"<MTSetVar name="search_id" op="++"></MTIf></MTSetVarTemplate>
<MTSetVar name="search_id" value="1">

2.強調させたい部分にIDを振る

次に、検索結果一覧の部分で、強調させたい部分を何らかの要素で囲み、その要素の開始タグのタグ名の直後に以下のような記述を入れます。

<MTGetVar name="add_search_id">

例えば、Movable Typeの標準テンプレートセットで、検索結果で記事のタイトルと本文の部分で、キーワードを強調表示したいとします。
これらは「ブログ記事の概要」のテンプレートモジュールで出力されていて、タイトルはh2タグ、本文はdivタグで囲まれています。
これらの部分を、それぞれ以下のように書き換えます(赤字の部分を追加)。

・記事のタイトル
<h2<MTGetVar name="add_search_id"> class="asset-name entry-title"><a href="<$MTEntryPermalink$>"><$MTEntryTitle$></a></h2>
・記事の本文
<div<MTGetVar name="add_search_id"> class="asset-body">
    <$MTEntryBody$>
</div>

この処理によって、強調表示対象の部分に、「search_1」「search_2」・・・などの連番のIDが振られます。

3.JavaScriptの追加

まず、「検索結果」のシステムテンプレートで、検索結果の一覧を表示する部分(「<MTSearchResults>」~「</MTSearchResults>」タグ)の直後に、以下のJavaScriptを追加します。
このJavaScriptは、「search_1」「search_2」・・・などの連番のIDがついた要素を順に取り出して、その内部のテキストで、キーワード部分をstrongタグで囲む処理をするものです。

<script type="text/javascript">
//<![CDATA[
(function(){
    var rex1, rex2;

    var keyword_org = '<$MTSearchString encode_js="1"$>';
    keyword_org = keyword_org.replace(/"/g, '');
    keyword_org = keyword_org.replace(/\s*(or|and)\s*/gi, '|');
    var ptn = '(' + keyword_org + ')';
    rex1 = new RegExp(ptn);
    rex2 = new RegExp(ptn, 'g');
    for (var i = 1; i < <MTGetVar name="search_id">; i++) {
        recursiveParse(document.getElementById('search_' + i));
    }

    function recursiveParse(pNode) {
        var i, cNode, txt, txtRep;

        var cNodes = pNode.childNodes;
        for (i = 0; i < cNodes.length; i++) {
            cNode = cNodes[i];
            if (cNode.nodeType == 1) {
                recursiveParse(cNode);
            }
            else if (cNode.nodeType == 3) {
                txt = cNode.nodeValue;
                txtRep = txt.replace(rex2, '<span class="search-keyword">$1</span>');
                if (txt != txtRep) {
                    var sNode = document.createElement('span');
                    sNode.innerHTML = txtRep;
                    cNode.parentNode.replaceChild(sNode, cNode);
                }
            }
        }
    }
})();
//]]>
</script>

4.強調表示のカスタマイズ

3.のJavaScriptでは、強調表示する部分をstrongタグで囲むようにしています。
strongタグにスタイルシートを割り当てれば、強調表示の見せ方をカスタマイズすることができます。

また、ブログ記事の中でstrongタグを使っていると、検索のキーワード以外の部分も強調表示されてしまいます。
この場合は、強調表示する部分を他のタグで囲む必要があります。

例えば、強調表示する部分を、「<span class="search-keyword">~<span>」で囲むようにしたいとします。
この場合、JavaScriptのstrongタグの処理の部分を、以下のように書き換えます。

txt = txt.replace(rex2, '<span class="search-keyword">$1</span>');