2004年11月 4日

JavaScript でインクリメンタル grep 検索

JavaScriptで grep 検索をしようと思い、次のようなコードを書いた。
function grep (pattern) {
    try {
        var regex = new RegExp(pattern, "i");
        var spans = document.getElementsByTagName('span');
        var length = spans.length;
        for (var i = 0; i < length; i++) {
            var e = spans[i];
            if (e.className == "line") {
                if (e.innerHTML.match(regex)) {
                    e.style.display = "inline";
                } else {
                    e.style.display = "none";
                }
            }
        }
    } catch (e) {
        // 正規表現の文法エラーを無視する
    }
}
 

検索対象とする各行を <span class="line">...</span> で囲って おいて、pattern にマッチした以外の行を非表示にするという手法 を取っている。 このような関数を定義しておいて、form の onsubmit に grep を 呼び出すコードを書いておけば grep のできあがりである。 正規表現を使った検索もできる。

<form onsubmit="grep(this.pattern.value); return false;">
<input type="text" name="pattern">
</form>

これはなかなかいいかもと思い、 miyagawa氏にどうよと 見せたところ、input の onkeyup でやればインクリメンタル検索 になるんじゃないの、と指摘された。 やってみたら意外とちゃんと動いた。

Grep: (def などと打ってみてください)

# Ruby 1.8.2 (2004-08-24) に付属の pp.rb から抜粋:
   346        class Struct
   347          def pretty_print(q)
   348            q.group(1, '#<struct ' + self.class.name, '>') {
   349              q.seplist(self.members, lambda { q.text "," }) {|member|
   350                q.breakable
   351                q.text member.to_s
   352                q.text '='
   353                q.group(1) {
   354                  q.breakable ''
   355                  q.pp self[member]
   356                }
   357              }
   358            }
   359          end
   360
   361          def pretty_print_cycle(q)
   362            q.text sprintf("#<struct %s:...>", self.class.name)
   363          end
   364        end
   365
   366        class Range
   367          def pretty_print(q)
   368            q.pp self.begin
   369            q.breakable ''
   370            q.text(self.exclude_end? ? '...' : '..')
   371            q.breakable ''
   372            q.pp self.end
   373          end
   374        end

上の例では form の onsubmit ではなく、 input の onkeyup で grep を呼び出している。

<form onsubmit="return false;">
<input type="text" name="pattern" onkeyup="grep(this.value)">
</form>

このままだと行数が増えると反応が遅くなるの で、そういう場合は JavaScript でハイライト のときと同様に途中の処理をキャッシュしてやれば多少はましになる。 簡単なキャッシュを導入するだけでも 1,000~2,000行程度なら IE, Firefox ともに実用的な速度で検索できるようだった。

追記
2004-11-08: if の条件が間違っていたので直しました。 それから、ライセンスについて訊かれましたが、自由に使ってください。 短いし、誰が書いても同じような感じになると思いますので。
2005-04-05: for (i in spans) という構文が Safari だと動かないという 指摘をいただいたので length = spans.length; for (i = 0; i < length; i++) に修正しました。

JavaScript でインクリメンタル 検索 (grepではない版) に続きを書きました。