jQueryにif機能を追加する
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ ちゃんとifを1機能としてつくるとjQueryの構造書きかえることになった
◆ findなどでstackに積まれる機能を利用して作ったので良さそう?
◆ findなどでstackに積まれる機能を利用して作ったので良さそう?
ifメソッド欲しい
jQueryのメソッドチェーンでかけるところはいいところだと思ってますですが 書いてるとifが欲しいと思ってきます
var jqelem = $(".sample")
flag1 && jqelem.attr("id", id)
!flag1 && jqelem.append(chldelem)
flag2 && flag3 && jqelem.removeClass(cls)
メソッドチェーンしたい!flag1 && jqelem.attr("id", id)
!flag1 && jqelem.append(chldelem)
flag2 && flag3 && jqelem.removeClass(cls)
$(".sample")
.if(flag1)
.attr("id", id)
.else()
.append(chldelem)
.endif()
.if(flag2 && flag3)
.removeClass(cls)
.if(flag1)
.attr("id", id)
.else()
.append(chldelem)
.endif()
.if(flag2 && flag3)
.removeClass(cls)
こんな感じに書きたいです
ですが この書き方をできるようにできるifメソッドをextendでつくるのは無理そうです
if や else で今がメソッドを実行するかの情報を持っておいて すべてのメソッドで実行時に確認する必要があります
さらに if 文のネストを考えるとtrue/falseの値をスタック状に持っておかないといけないです
普通 if をjQueryで作るとすると↓のようになりそうです
$(".sample").if(flag1, function truefn($this){
$this.attr("id", id)
}, function falsefn($this){
$this.append(chldelem)
}).if(flag2 && flag3, function truefn($this){
$this.removeClass(cls)
})
これだとifメソッドだけで完結しています$this.attr("id", id)
}, function falsefn($this){
$this.append(chldelem)
}).if(flag2 && flag3, function truefn($this){
$this.removeClass(cls)
})
第一引数によって第二引数か第三引数の関数があれば実行するだけです
ですが これだとメソッドチェーン感が減るので上のみたいにしたいです
なんとか作ってみる
var obj = {
if: function(v){
this._if_stack || (this._if_stack = []);
this._if_stack.push(!!v);
return this;
},
endif: function(){
this._if_stack.pop();
return this;
},
else: function(){
this._if_stack.push(!this._if_stack.pop());
return this;
},
unless: function(){
this.if(!v);
return this;
},
};
var jqproto = $.fn.init.prototype;
for(var i in jqproto){
if(typeof jqproto[i] === "function"){
obj[i] = function(name){
return function(){
this._if_stack || (this._if_stack = []);
if(this._if_stack.length === 0 || this._if_stack.slice(-1).pop()){
return jqproto[name].apply(this, arguments);
}
return this;
};
}(i);
}
}
obj.__proto__ = jqproto;
$.fn.init.prototype = obj;
if: function(v){
this._if_stack || (this._if_stack = []);
this._if_stack.push(!!v);
return this;
},
endif: function(){
this._if_stack.pop();
return this;
},
else: function(){
this._if_stack.push(!this._if_stack.pop());
return this;
},
unless: function(){
this.if(!v);
return this;
},
};
var jqproto = $.fn.init.prototype;
for(var i in jqproto){
if(typeof jqproto[i] === "function"){
obj[i] = function(name){
return function(){
this._if_stack || (this._if_stack = []);
if(this._if_stack.length === 0 || this._if_stack.slice(-1).pop()){
return jqproto[name].apply(this, arguments);
}
return this;
};
}(i);
}
}
obj.__proto__ = jqproto;
$.fn.init.prototype = obj;
すごく無理矢理やってます
$() で返ってくるjQueryオブジェクトがメソッドを呼ぶときは__proto__を辿って $.fn.init.prototype を見ています
そこを
{$()} ---> {$fn.init.prototype}
{$()} ---> {ラップ用オブジェクト} ---> {$fn.init.prototype}
のようにプロトタイプチェーンを書き換えます{$()} ---> {ラップ用オブジェクト} ---> {$fn.init.prototype}
そして $fn.init.prototype にあるプロパティからすべての関数を 同じ名前でラップ用オブジェクトにプロパティを作ります
中身はstackの一番上を見てtrueなら $fn.init.prototype の同じ名前の関数を実行して false なら何もしないこれだけです
if, else, endif, unless のメソッドでは引数を見てスタックを編集だけします
スタックはjQueryオブジェクトごとに必要なのでなければ作るようにしておきます
これだけのためにjQueryの構造書き換えてるわけなので 良い拡張とは呼べそうにないです
ifメソッドを作るタイミングで そのときに存在する全関数をラップするようにしてますが その後にextendで追加されたものは対応できてないです
もっといい方法は・・・
なんかいい方法は~~・・・
んーー・・・・
・・・・・・・・・
・・・・・・・・・・・・
・・・・・・・・・閃いた!
jQueryってjQueryオブジェクトが空でもメソッドは実行できてその場合は何も実行されないです
それに find などでは stackに積まれて end でstackから降ろされます
これは使える!!!
$.fn.extend({
if: function(ifval){
if(ifval){
return this.filter("*")
}else{
return this.eq(this.length)
}
},
elseif: function(ifval){
return this.else().if(ifval)
},
else: function(){
var is_if_true = this.length > 0
return this.end().if(!is_if_true)
},
});
if: function(ifval){
if(ifval){
return this.filter("*")
}else{
return this.eq(this.length)
}
},
elseif: function(ifval){
return this.else().if(ifval)
},
else: function(){
var is_if_true = this.length > 0
return this.end().if(!is_if_true)
},
});
if では 引数がtrueなら中身そのままでstackに積まれるようにfilterですべての "*" を指定しています
false なら空になるように eq で存在しないところを指定しています
else では stackから1つおろして 逆の値をstackに積むようにします
endif の代わりが end で本来の機能そのままで使えます
elseif というのも作りましたが 中身は else と if を順番にやってるだけです
必要な end の数が増えてわかりづらいので使わないほうがいいかと思います
if().else().if().end().end() と同じなので if().elseif().end().end() としないといけないです
jQueryのstack機能を使ってるだけなので if や end の対応がずれるとおかしな動きをしてしまいますし 特にエラーも出ないので対応には気をつけないといけないです
また プロトタイプチェーン書き換えの方法と違って最後に end がないと返り値が空になる場合があります
こっちの方法だと余計なところに手を加えてないので問題なく使えそうです
たぶん
結局
プロトタイプチェーン書き換えの方法では 各メソッド実行時にifの条件となる値を確認していたので 関数を条件として渡して毎回実行と拡張することもできますそれができると if中にフラグとなる値を更新させて途中から実行するしないを変更したり else の方も実行したくなったときに両方実行させるなんてこともできます
高機能にはなりそうですが jQueryの構造書き換えてまでやる必要があるのか 微妙なところです
普通にifを使うだけなら下の方法でいいかと思います