2013.03.14
jQuery 重いらしいcontainsフィルターを書き換えてみる
jQueryの独自フィルターは重いらしい…。
書籍でもお世話になっているこちらのサイト様を参考にへっぽこスクリプトの改善を日々図っている訳ですが。
jQueryを良くする25のTIPS[to-R]
確かにOじろーんちの細い回線では:contains()をセレクターに使っている当ちゅんもす置き場のキノコが一瞬みえちゃう。
WordPressのloopでコメント数を表示してるですが、コメントがある時はキノコを表示。ノーコメントの時はキノコを隠蔽してありますw。
テンプレートタグcomments numberの引数にコメント0の場合にnを、他は数値のみを出力させるようにして、フィルター:containsでnを拾って.remove()。
ちょっと方法を変えました〜WordPress キノコは最初から隠すべき キリッ!(コメント件数の表示)
なぜcssでdisplay:none;にしないのかは、無効な表記がHTMLで繰り返されるのが煩わしかったというか、大した理由ではないのですがなんとなく。
1 2 3 |
<div class="comments-link"> <a href="<?php comments_link(); ?>"><?php comments_number('n', '1', '%'); ?></a> </div> |
1 2 3 4 |
$(function(){ var comm_l = $('.comments-link','#main'); comm_l.find('a:contains("n")').remove(); }); |
ちょっとハードルが高い気もしますが、勉強がてらなんとか書き換えてみようと思います。
filterを使ってみる
やりたいことは、コメントリンクの中に "n"という文字がある時、表示しない。
まずは文字を探す方法から。
1 2 3 |
<div class="comments-link"> <a href="https://chun-oki.sw8field.com/webworks/20130213/1113.html#comments">n</a> </div> |
htmlはこんな感じで書き出されています。divの中にa そしてテキスト。
.findでaを見つけるところまでは現状と同じ、そこからfilterで文字の合致をふるい分けできないかやってみました。
filterの引数のコールバック関数でtextを取得して、比較すればいいような気がする…
1 2 3 |
comm_l.find('a').filter(function () { return $(this).text()=='n'; }); |
filterの戻り値はObject[a 1113.html#comments]となったし、どうやらできたっぽい…
しかし、スペースや改行が入るとだめな模様。改行や空白が入るブラウザがあるとの情報もありもう一工夫必要ですな。
噂には聞いていた”正規表現”とやらで一致とか、含むとか、ができるとか…
1 2 3 4 5 6 |
$(function(){ var comm_l = $('.comments-link','#main'); comm_l.find('a').filter(function () { return $(this).text().match(/n/m); }).parent().remove(); }); |
これで□□nでも<br>nでも大丈夫なはず……。
スラッシュで囲うと正規表現となり、オプションmでマルチラインを検索となるらすぃ…。
.parentでaの親要素であるcomm_lに戻って.remove() できた(∩・ω・)∩
正規表現についてはこちらでもっと勉強したいと思うのです。
JavaScript初級者から中級者になろう:四章第六回 正規表現
ちょっと考察…疑惑のスクリプト
でも、.matchの戻り値は配列とのことで一文字しか無いし戻り値は["n"]なんじゃなかろうか?という疑問も。
なんもない時はnullが返る。
これって、.filterが真偽値で判定してるとしたらどうなんでしょう?
要素集合から、引数で渡したコールバック関数で合致と判定しなかった要素を全て削除したものを返す。
この関数は、全ての要素に対して $.each のように順に実行されます。この時にfalseを返せば、その要素は集合から外される。
false以外の値を返せば、その要素は残る。
filter(fn) – jQuery 日本語リファレンスさんより引用
false以外の値を返せば、その要素は残る。という部分が気になります。
nullはfalseじゃないよなぁ。3行目はもしかして、
1 |
return $(this).text().match(/n/m) == "n"; |
と書かなくてはならないんだろうか?
しかし、いかにも冗長な気がします。
このメソッドの2番目の構文は、セレクタではなく関数によって要素をフィルタリングができます。要素ごとにその関数を実行し、true(もしくは、真偽値としてtrueと同等の値)を返した場合、その要素は選択されます。そうではない場合、除外されます。
.filter() – jQuery API Documentation 日本語訳さんより引用
ともあり、Firefox、Safariでは動作確認できてますし、まあ、いいかという気が激しくしています。ちゅんもす置き場では.matchのまま行こうかと。
精神上よくない気もするのでもうちょっと書いてみます。Javascriptの.testメソッドは真偽値が返ります。
1 2 3 4 5 6 |
$(function(){ var comm_l = $('.comments-link','#main'); comm_l.find('a').filter(function () { return /n/m.test($(this).text()); }).parent().remove(); }); |
今回は1文字しか判定してないので真偽明白ですが、’先頭にxxxを含む文字列’とか条件がもっと複雑だったりする場合、.filterするにはこちらの方がいいのかなぁ。
なにはともあれ、速くなるのか?
FirefoxのアドオンFirebugのコンソールで計測してみました。
1 2 3 4 5 6 |
console.time('mochi'); var comm_l = $('.comments-link'); for (i = 0; i < 1000; i++) { comm_l.find("a:contains('n')").append('もち'+i); } console.timeEnd('mochi'); |
.find("a:contains('n')")と記述した場合0.689秒かかっております。
1 2 3 4 5 6 7 8 |
console.time('mochi'); var comm_l = $('.comments-link'); for (i = 0; i < 1000; i++) { comm_l.find('a').filter(function () { return $(this).text().match(/n/m); }).append('もち'+i); } console.timeEnd('mochi'); |
0.186秒!!!!!
ぐわーっ!速い!
よし!これで行こう!!
ps.当ブログがなんかおかしなことになっていたら教えて下さい…