フォーカスイベントとタブ切り替え
input や textarea にフォーカスがあったってる状態でタブを切り替えたときに focus, blur イベントがどのように起こるか各ブラウザを調べてみた。
テスト内容
ページには input[type=text], textarea, button が並んでる。
各要素はフォーカスすると 要素名を書き出し、フォーカスが外れると 要素名: blur と書き出すようになっている。
やったことはテストページと別のページを開いておき、input or textarea or button のいづれかの要素にフォーカスした状態でもう一方のページに切り替え、テストページに戻るというテスト。ページの切り替えはタブバーを使った。
テストしたブラウザは IE 7, Firefox 3.0.6, Opera 9.64, Safari 3.2.2 の 4 つ。いづれも Windows 環境。
結果
Opera
テストページから別のページを切り替えてもフォーカスは外れない。
戻ってきてもフォーカスされたまま。
ページ切り替えでイベントは発生しない。
$.each の落とし穴
jQuery の $.each は for で書くよりさくっとかけて便利だ。 *1
使い方は
$.each([0, 1, 2], function() { alert(this); })
のように、第1引数に Array (or Object)、 第2引数に各要素に対する処理 Function を書く。
this は それぞれの値(この例だと 0, 1, 2)が入ってくるんだけど、ここで注意すべき点は型が Number から Object に変換されること*2。
つまり下のようなコード*3だと、'a', 'b', 'c' と順にアラートすると思ったら 'hoge' としかアラートされない。これは、switch が '===' で比較しているため。
$.each(['a', 'b', 'c'], function(){ alert(typeof this) // object switch(this) { case 'a': alert('a') break; case 'b': alert('b') break; case 'c': alert('c') break; default: alert('hoge') break; } })
型変換されずに元の値をそのまま使いたい場合は
$.each([0, 1, 2], function(i, v) { alert(typeof v) // number alert(v) }
このように、$.each の 第2引数の関数の第2引数 (上の例だと v)を扱う必要がある。ちなみに 第一引数 (上の例だと i)はインデックス(key) が入る。
$.each 内で switch や === を使って this を比較するときは気をつけようね。
追記 1:10
Number or String が Object に変換されるってのはつまり
var n = 3 s.apply(n) function s { alert(this) // 3 alert(typeof this) // object }
こういうことなんですね。
jQuery でしましまを作るプラグイン
ヒント: http://h2ham.seesaa.net/article/114037411.html
each 内の function の引数にインデックスがわたされることがわかったので
(function($){ $.fn.zebra = function(options){ var default_options = { class_name: 'zebra', interval: 2 }; options = $.extend(default_options, options || {}); return this.each(function(i){ if((i + 1) % options.interval == 0) $(this).addClass(options.class_name); }) } })(jQuery);
使い方
// li 3つおきに 'zebra' というクラスをつける $('#zebra-ul li').zebra({interval: 3}); // tr 2つおきに 'shimashima' というクラスをつける $('#zebra-table tbody tr').zebra({class_name: 'shimashima');
jQuery プラグインを書くときのポイント
input に事前に値を入力しておく - higeorange's blog を例に jQuery のプラグイン(メソッド追加)書き方のポイントを書いて見る。(あくまでも私の書き方)
メソッドの追加法
(function($) { $.fn.[メソッド名] = function() {} })(jQuery)
という書き方をしているけど、
jQuery.fn.[メソッド名] = function() {}
でも
jQuery.fn.extend({ '[メソッド名]': function() {} })
でもいけるので好きな書き方で。
メソッドの引数
$.fn.preInput = function(txt, options) { var default_options = { class_name: 'pre-input' }; // options = $.merge(default_options, options || {}); options = $.extend(default_options, options || {});
ここで重要なのは "$.merge" "$.extend"。
重要な引数(ここでは txt)はひとつの引数として、その他はあってもなくてもいいようにオブジェクトとしている。
そのオブジェクトとして渡された引数を $.merge $.extend メソッドで デフォルトで指定した変数を上書きしている。
$.merge $.extend 便利!!
追記修正 2009/2/0 23:25
$.merge メソッドではなく $.extend メソッドでした。
ややこしい。
jQuery オブジェクトを返す
jQuery のメソッドはほぼすべてが jQuery オブジェクトを返す。*1
jQuery オブジェクトを返すと何が便利かというと、
$('p').css('color', 'red').hover(function() { alert('hover') }, function() {})
のようにメソッドを繋げて書けること。
jQuery オブジェクトを返すために
return this.each() // each メソッドも jQuery オブジェクトを返す。
としている。
each メソッド: 各要素に対する処理
this.each(function() { ... var elm = $(this); ... });
each メソッド内で $() で指定した各要素に対する処理をかいていくのだけど、ここで注意することは each(function() {}) 内の this は jQuery オブジェクトではなく 各エレメントそのものであるということ。
つまり、その要素に対して jQuery のメソッドを使いたければ上のように $(this) とする必要がある。
まとめ
4 点ほど書いたけど、この中でもっとも jQuery らしくするのは 3 番目に書いた jQuery オブジェクトを返すということだと思う。
jQuery はそれ自体で便利なメソッドが十分用意されているので、それらを組み合わせてちょこちょこっと手を加えればそれらしいものが簡単にできる。お試しあれ。
*1:例外: get メソッド
input に事前に値を入力しておく
input に事前に入力値を入力しておいてフォーカスするとその値が消える jQuery プラグインを書いた。
(function($) { $.fn.preInput = function(txt, options) { var default_options = { class_name: 'pre-input' }; options = $.extend(default_options, options || {}); return this.each(function(){ if(typeof this.value == 'undefined') return; var elm = $(this); elm.val(txt) elm.addClass(options.class_name) elm.focus(function(){ if(elm.val() == txt) { elm.removeClass(options.class_name) elm.val(''); } }); elm.blur(function(){ if(elm.val() == '') { elm.addClass(options.class_name) elm.val(txt) } }); }); } })(jQuery);
使い方
<input id="hoge" type="text" value="" />
$('#hoge').preInput('ここに入力してね');
デモ
一つ前の jQuery.eventDelay とあわせてデモを作った。
http://labo.higeorange.com/jquery/#preinput
追記 修正 2009/2/10 0:10
コード 8 行目
$.merge -> $.extend
Zooomr 用 Autopagerize SITEINFO 書いた
フォトーク
{ "name": "Zooomr photalk", "data": { "pageElement": "//table[@id=\"timeline\"]", "url": "http://(jp|www).zooomr.com/*", "nextLink": "//table[@id=\"timeline\"]/following-sibling::h2[1]/a[last()]", "exampleUrl": "http://jp.zooomr.com/" } }
nextLink 修正 23:30
写真ページ
{ "name": "Zooomr photos", "data": { "pageElement": "//table[@id=\"SubNav\"]/following-sibling::table[1]/tbody[1]/tr[1]/td[1]/table[1]", "url": "http://(jp|www).zooomr.com/photos*", "nextLink": "//a[@class=\"Next\"]", "exampleUrl": "http://jp.zooomr.com/" } }
追記
wedata に登録しておいた。
Ajax でフォームポスト
(function($) { $.fn.ajaxPost = function(callback, options) { var default_options = { data_type: 'html', before_send: function() {}, error_handler: function() {} }; options = $.extend(default_options, options) return this.each(function(){ if(this.tagName.toLowerCase() != 'form') return; var f = $(this); var submit = f.find('button[@type="submit"], input[@type="submit"]') f.submit(function(e) { e.preventDefault(); $.ajax({ url: f.attr('action'), type: 'POST', data: f.serialize() + '&mode=ajax', dataType: options.data_type, beforeSend: function() { submit.attr('disabled', 'disabled') options.before_send(); }, success: callback, error: options.error_handler, complete: function(xhr, textStatus) { submit.removeAttr('disabled') } }); }); }); } })(jQuery)
使い方
<form action="/hoge" id="hoge"> ..... </form>
$('#hoge').ajaxPost(function(data, textStatus) { alert(data); });
追記 2009/2/12 22:50
修正。complete オプションがあったのね。