jQueryに関する質問です。
以前別のQ&Aサイトでも質問したのですが、回答を頂けなかったので、
こちらで質問させていただきました。
それぞれ独立したjQueryプラグイン、AとBがあって、
$(セレクタ).A().B();
という風にメソッドチェーンでそれぞれのプラグインを呼び出しているのですが、
Aのプラグインの処理結果によって、Bの処理を実行するかどうかを変更したいのですがどうすればよろしいでしょうか。
質問がわかりづらく恐縮ですが、
具体的には、Aでformのバリデーションチェックをし、
Bで確認画面を表示という風にしたいのですが、
バリデーションをクリアしていない場合、
確認画面を表示させないようにしたく質問させていただきした。
下記、簡略化したサンプルコードを作成してみましたので、
どうぞ、ご意見・アドバイス等頂けますようお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
JavaScriptに関するイベントの伝播関係のミスが原因です
解説
- プラグインAとBがそれぞれフォームのサブミットに対してイベント登録
- 「確認する」ボタンを押された時にsubmitイベントが発行される
- イベントの処理順番は登録順、つまりA→Bの順
- Bはsubmitイベントが実行されたタイミングで動作確定、つまりAの戻り値なんて関係なく実行される
対策
これ読んでください。
http://www.webcyou.com/?p=4528
jQueryを使った場合→無理
プラグインAの中身を下記のように変更すれば意図通りの動作になるはず…
ですが、think49さんに指摘&調査して頂いた内容により上手く動作せず、やはり確認ダイアログは表示されてしまいます。
plugin_A.js
1(function($) { 2 $.fn.plugin_A = function() { 3 this.submit(function(event) { // ここにevent引数を用意 4 if ($("input", this).val()) { 5 $(".errMsg").hide(); 6 }else{ 7 $(".errMsg").show(); 8 event.stopPropagation(); // これで良いはず…正常動作しないだと!? 9 } 10 return false;//ブラウザのsubmitイベントの中止 11 }); 12 return this; 13 };
addEventListenerを使った場合→無理
全く同じロジックを、addEventListnerを使って実装します。
一度 this.each
をかませて置くことで内部のthisはDomエレメントになるので…
plugin_A.js
1(function($) { 2 $.fn.plugin_A = function() { 3 this.each(function() { 4 this.addEventListener('submit', function(event){ 5 console.log("plugin_A submit!"); 6 if ($("input", this).val()) { 7 $(".errMsg").hide(); 8 } else { 9 $(".errMsg").show(); 10 event.stopPropagation(); 11 event.preventDefault(); 12 return false; 13 } 14 }, false); 15 }); 16 return this; 17 }; 18})(jQuery);
plugin_B.js
1(function($) { 2 $.fn.plugin_B = function() { 3 this.each(function() { 4 this.addEventListener('submit', function(event){ 5 console.log('plugin_B submit!'); 6 return confirm('送信しますか?'); 7 }, false); 8 }); 9 return this; 10 }; 11})(jQuery);
はい、エラーメッセージと、確認ダイアログが出たので失敗です。
click -> submitのイベント伝播に変更
更にプラグインAのネストが進んで超キモくなりましたが、
なんとか達成できました。
plugin_A.js
1(function($) { 2 $.fn.plugin_A = function() { 3 this.each(function(){ 4 var $self = $(this); 5 $self.find('button').each(function(){ 6 this.addEventListener('click', function(event){ 7 console.log("plugin_A submit!"); 8 if ($("input", $self).val()) { 9 $(".errMsg").hide(); 10 } else { 11 $(".errMsg").show(); 12 // event.stopPropagation(); 13 event.preventDefault(); 14 return false; 15 } 16 }, false); 17 }); 18 }); 19 return this; 20 }; 21})(jQuery);
Input要素内でEnterを押してサブミットさせたらどうなるかと思って試しましたが、
サブミットボタンをクリックした扱いになって意図どおりの動作になります。
結論
submit -> submit のイベント伝播を食い止める方法は不可能なようです。
click -> submit にすることで可能でした。
追記:
誤りがあったので修正しています。
think49さんに指摘&調査していただいた内容を元に解決法を別口で記載しています。
投稿2016/02/12 09:11
編集2016/02/13 06:34総合スコア21145
0
miyabi-sun さんが指摘されるように submit
イベントの伝播を止めれば2つめの submit
イベントを発動させる事を回避できます。
他にカスタムイベント(例: confirm-submit
イベント)を作り、submit
イベントは常に event.preventDefault()
して confirm-submit
イベントから条件に応じて動的に発火させて form.submit()
する方法が考えられますが、イベント伝播を止める方がスマートですね。
その他、実装法はいろいろありますが、invalid
イベント、setCustomValidity(), checkValidity()
等、HTML5 で追加された API を利用するとスマートにかけると思います。
HTML
1<form> 2 <p><label>名前<input name="name" type="text" required /></label></p> 3 <input type="submit" value="submit"> 4</form> 5<script> 6'use strict'; 7/** 8 * plugin-A 9 **/ 10jQuery.fn.setValidateForm = (function (jQuery, handleSubmit, handleInvalid) { 11 return function setValidateForm () { 12 for (var i = 0, l = this.length, form; i < l; ++i) { 13 form = this[i]; 14 15 if (form.tagName === 'FORM') { 16 jQuery(form).on('submit', handleSubmit); 17 18 for (var j = 0, elements = form.elements, m = elements.length, element, errMsg; j < m; ++j) { 19 element = elements[j]; 20 21 if (element.willValidate) { 22 jQuery(element).on('invalid', handleInvalid); 23 } 24 } 25 } 26 } 27 28 return this; 29 } 30}(jQuery, function handleSubmit (event) { 31 var form = event.target; 32 33 if (!form.checkValidity()) { 34 event.stopPropagation(); 35 event.preventDefault(); 36 } 37}, 38function handleInvalid (event) { 39 var element = event.target, 40 validity = element.validity; 41 42 if (validity.valueMissing && element.required) { 43 element.setCustomValidity('必須項目です(カスタムエラーメッセージ)'); 44 } 45})); 46 47/** 48 * plugin-B 49 **/ 50jQuery.fn.setConfirmSubmitForm = (function (jQuery, handleSubmit) { 51 return function setConfirmSubmitForm () { 52 for (var i = 0, l = this.length, form; i < l; ++i) { 53 form = this[i]; 54 55 if (form.tagName === 'FORM') { 56 jQuery(form).on('submit', handleSubmit); 57 } 58 } 59 60 return this; 61 }; 62}(jQuery, function handleSubmit (event) { 63 var form = event.target, 64 window = form.ownerDocument.defaultView; 65 66 if (!window.confirm('送信しますか?')) { 67 event.preventDefault(); 68 } 69})); 70 71jQuery('form').setValidateForm().setConfirmSubmitForm(); 72</script>
(2016/02/14 08:18追記)
jsfiddleサンプルで jquery.js を読み込む設定になっていなかった不具合を修正しました。
Re: stack-overflow さん
投稿2016/02/12 14:24
編集2016/02/13 23:19総合スコア18156
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/02/15 06:15
0
plugin_A にバリデーションクリア時だけ起動する、関数を渡してみてはどうでしょう?
Javascript
1(function($) { 2 $.fn.plugin_A = function(success) { 3 this.submit(function() { 4 if (!$("input", this).val()) { 5 $(".errMsg").show(); 6 return false;//ブラウザのsubmitイベントの中止 7 } 8 9 $(".errMsg").hide(); 10 // 渡された関数を起動 11 return success(); 12 }); 13 return this; 14 }; 15})(jQuery);
Javascript
1$(function() { 2 $('form').plugin_A(function() { 3 return confirm('送信しますか?'); 4 }); 5});
投稿2016/02/12 12:11
総合スコア4666
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
上げられたサンプルコードの場合、$('form').plugin_A()
の返り値がtrue
あるいはfalse
となるので、さらにメソッドチェーンをつなげること自体ができません。
$('form').plugin_A()
の返り値をif文で受けて、残りの処理を書くしかありません。
投稿2016/02/12 07:01
総合スコア145064
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/02/12 08:01 編集
2016/02/12 12:26
2016/02/13 15:42
2016/02/18 03:01
0
ベストアンサー
はじめに
jQueryで同一階層に登録された複数のイベントを中断させる場合は、
イベントハンドラ内で event.stopImmediatePropagation()を呼び出します。
jQuery,API Documents,event.stopImmediatePropagation
説明
イベントハンドラがfalseを返した場合「preventDefault()とstopPropagation()」が
呼ばれるのですが、jQuery内で独自にイベントを呼び出すループがあり、その条件に
ImmediatePropagationのフラグ判定があります。(一部抜粋)
javascript
1i = 0; 2while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { 3 event.currentTarget = matched.elem; 4 5 // このループ 6 j = 0; 7 while ( ( handleObj = matched.handlers[ j++ ] ) && !event.isImmediatePropagationStopped() ) { 8 9 // Triggered event must either 1) have no namespace, or 2) have namespace(s) 10 // a subset or equal to those in the bound event (both can have no namespace). 11 if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { 12 13 event.handleObj = handleObj; 14 event.data = handleObj.data; 15 16 ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || 17 handleObj.handler ).apply( matched.elem, args ); 18 19 if ( ret !== undefined ) { 20 if ( ( event.result = ret ) === false ) { 21 event.preventDefault(); 22 event.stopPropagation(); 23 } 24 } 25 } 26 } 27}
内側のwhileで、!event.isImmediatePropagationStopped()を条件としているため、
イベントハンドラが、falseを返しただけでは中断されず同一階層の次のイベントを
呼び出します。
まとめ
サンプルコードに適用した場合は、以下のようにすると実現できるかと思います。
1)イベントハンドラで、eventオブジェクトを受け取るようにする。
2)バリデーションでエラーとなる処理で、event.stopImmediatePropagation()を呼び出す。
かつ、イベントハンドラの戻り値を、falseで終了する。
※サンプルコードがバリデーションの結果に関係なく、falseを返していたので移動しました。
javascript
1(function($) { 2 $.fn.plugin_A = function() { 3 this.submit(function(event) { 4 if ($("input", this).val()) { 5 $(".errMsg").hide(); 6 return true; 7 }else{ 8 $(".errMsg").show(); 9 event.stopImmediatePropagation(); 10 return false; //ブラウザのsubmitイベントの中止 11 } 12 }); 13 return this; 14 }; 15})(jQuery);
投稿2016/02/12 23:03
編集2016/02/12 23:21総合スコア263
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/02/13 16:11
2016/02/18 03:02
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/02/12 15:06
2016/02/12 15:49
2016/02/12 18:18
2016/02/13 06:37 編集
2016/02/13 16:08