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 でやればインクリメンタル検索 になるんじゃないの、と指摘された。 やってみたら意外とちゃんと動いた。
# 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++) に修正しました。