質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Q&A

解決済

5回答

3429閲覧

jQueryに関する質問です。

stack-overflow

総合スコア29

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

4グッド

0クリップ

投稿2016/02/12 06:55

jQueryに関する質問です。

以前別のQ&Aサイトでも質問したのですが、回答を頂けなかったので、
こちらで質問させていただきました。

それぞれ独立したjQueryプラグイン、AとBがあって、
$(セレクタ).A().B();
という風にメソッドチェーンでそれぞれのプラグインを呼び出しているのですが、
Aのプラグインの処理結果によって、Bの処理を実行するかどうかを変更したいのですがどうすればよろしいでしょうか。

質問がわかりづらく恐縮ですが、
具体的には、Aでformのバリデーションチェックをし、
Bで確認画面を表示という風にしたいのですが、
バリデーションをクリアしていない場合、
確認画面を表示させないようにしたく質問させていただきした。

下記、簡略化したサンプルコードを作成してみましたので、
どうぞ、ご意見・アドバイス等頂けますようお願いいたします。

サンプルコード -Plunker-

mhashi, yng13, 5o5o_wagon, kuwako👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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
miyabi-sun

総合スコア21145

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2016/02/12 15:06

jQuery のイベントハンドラ関数は特殊で return false; は event.stopPropagation(), event.preventDefault() の両方が発動する仕様です。 従って、このコードでは常に event.stopPropagation() されます。 https://api.jquery.com/on/#event-handler あと、this.stopPropagation() が機能しない気がするのは気のせいでしょうか。 Google Chrome の DOM Inspector で確認しましたが、form要素ノードには stopPropagation プロパティが存在しないようなので event.stopPropagation() が正ではないかと…。
miyabi-sun

2016/02/12 15:49

「サンプルコード -Plunker-」を質問者さんが提示してくださってたので、 eventとかで試したのですが、どうにも動かないんですよね… this.stopPropagation()をサンプルAに仕込んだら動作したのでそれを書いてます。 …という訳でちゃんと見ろって話ですので調べてみました。 https://api.jquery.com/submit/ 今回質問者さんは$element.submit()の関数を利用してイベントを宣言していますので、 > so we can cancel the submit action by calling .preventDefault() on the event object or by returning false from our handler. submitメソッドでもfalseを返せば自動的にイベントが止まるように書かれてます。 でも質問者さんが提示してくれたコードは動かない・・・何故だ 再度試した所、eventにstopPropagationやpreventDefaultを仕込む事でイベントの伝播を食い止める事に成功しましたが、 プラグイン内部で動作させる場合、this.submitに代入した無名関数ではなく、 $.fn.plagin_aに代入する箇所の無名関数の第一引数にeventを仕込む必要がありました。 その辺でも動作がまた変わってくるのでしょうか…ちゃんとjQueryのソースコード読まないと闇に飲まれそうですね…
think49

2016/02/12 18:18

検証してみましたが、this.stopPropagation() は期待通り、TypeError になりました。 this.stopPropagation(); // TypeError: this.stopPropagation is not a function 更に検証してみたところ、どうやら jQuery の return false; は event.stopPropagation() を正しく実装していない(バグ)ようです。 上位ノードへのバブリングは抑制しますが、同一要素におけるイベント伝播は DOM API の event.stopPropagation() と違って停止せず、伝播してしまいます。 https://jsfiddle.net/torfh9f5/
miyabi-sun

2016/02/13 06:37 編集

確かに動作したと思ったら、JavaScriptエラーで止まってただけでした…! 流石にまずいので修正しました。 教わった内容を元に更に弄りましたが、 どう頑張ってもsubmit -> submitのイベント伝播は止められないようです。 結局代案としてclick -> submitのイベント伝播を止めるように修正しました。 大嘘が一生残る所で危なかったです・・・ありがとうございます!
stack-overflow

2016/02/13 16:08

回答及び検証、本当にありがとうございます。 正直なところ、私の知識不足のせいで理解が追いついておりませんが、 回答いただいた内容をひとつひとつ理解できるように勉強させていただきます。
guest

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
think49

総合スコア18156

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

stack-overflow

2016/02/13 16:18

回答ありがとうございます。 たくさんの方法をアドバイスいただき、大変勉強になりました。 まだ完璧には理解できておりませんが、 理解できるまでしっかり勉強させて頂きます。 本当にありがとうございます!
stack-overflow

2016/02/15 06:15

think49さん >jsfiddleサンプルで jquery.js を読み込む設定になっていなかった不具合を修正しました。 度々ありがとうございます。 私の認識ミスでしたら申し訳ございませんが、 作成頂いた、サンプルコードに関しまして、 一度でもエラーメッセージが表示されてしまうと、 その後、文字を入力した状態でsubmitボタンをクリックしても、 エラーメッセージが表示されてしまわないでしょうか。 現在勉強のため、コードを修正しようと一行ずつ調べておりますが、 ひとまず、コメントさせて頂きました。
guest

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

root_jp

総合スコア4666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

stack-overflow

2016/02/13 16:02

回答ありがとうございます。 そのような方法もあるのですね! 参考にさせていただきます。
guest

0

上げられたサンプルコードの場合、$('form').plugin_A()の返り値がtrueあるいはfalseとなるので、さらにメソッドチェーンをつなげること自体ができません。

$('form').plugin_A()の返り値をif文で受けて、残りの処理を書くしかありません。

投稿2016/02/12 07:01

maisumakun

総合スコア145064

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

stack-overflow

2016/02/12 08:01 編集

早速の回答ありがとうございます。 >さらにメソッドチェーンをつなげること自体ができません。 そうなんですね…。 >$('form').plugin_A()の返り値をif文で受けて、残りの処理を書くしかありません。 アドバイスありがとうございます。 var hoge = $('form').plugin_A(); if(hoge){ $('form').plugin_B(); } のように変更してみたのですが、 hogeには常にjQueryオブジェクトが帰ってきてしまい、 バリデーションをクリアしていなくてもplugin_Bが実行できてしまいます。 >$('form').plugin_A()の返り値がtrueあるいはfalseとなるので とありますが、どのようにすればバリデーションをクリアした場合はplugin_A()の返り値をtrueに、そうでなければfalseにすることができますでしょうか。 (そもそも作り方自体間違っているのでしょうか…) 度々恐れ入りますが、ご回答頂けますと幸いです。
think49

2016/02/12 12:26

> 上げられたサンプルコードの場合、$('form').plugin_A()の返り値がtrueあるいはfalseとなるので、さらにメソッドチェーンをつなげること自体ができません。 jQuery#plugin_A() は常に this を返すコードに読めます。 jQuery#submit() で return false; がありますが、イベントハンドラ関数内の定義なので jQuery#plugin_A() の返り値に影響しないのではないでしょうか。
stack-overflow

2016/02/13 15:42

>jQuery#plugin_A() は常に this を返すコードに読めます。 仰るとおりです。 当方、プログラミングの知識が乏しく、 はじめてのjQueryプラグイン作成で躓いてしまい、今回質問させていただいたのですが、 「jQuery プラグイン 作り方」などでググると、とりあえず最後に「return:this;」の「おまじない」を書いておけ!と頻繁に見かけたので、深く理解せずに進めておりました。 また、jQuery#submit() で return false;は、ブラウザ本来のsubmitイベントを止める為で、特に返り値には関係ございません。
miyabi-sun

2016/02/18 03:01

> 最後に「return:this;」の「おまじない」を書いておけ! jQueryのライブラリである以上、このおまじないは良い実装です。 プログラミングの関数は元々数学の世界で使われる関数からきたもので、 数学や世界の関数では、f(n)は関数fで加工されたnという意味となります。 だから関数fが必ずthisを返すのは如何なものか、n関係ねーじゃんという嫌悪感を感じる人も居ます。 しかしjQueryが必ずthisを返すメソッドチェーンという手法を採用している以上、 jQuery.fn.xxxの戻り値が常にthisになるのはjQueryの設計思想に基づく自然な実装です。 メソッドチェーンという設計思想自体がどうよってのはあります。 しかし皆が「ボクの考える最強の実装」でやると非常に扱いにくい歪なものになります。 嫌ならjQueryではない別のライブラリやフレームワークを使えば良いだけの話です。
guest

0

ベストアンサー

はじめに
jQueryで同一階層に登録された複数のイベントを中断させる場合は、
イベントハンドラ内で event.stopImmediatePropagation()を呼び出します。
jQuery,API Documents,event.stopImmediatePropagation

説明
イベントハンドラがfalseを返した場合「preventDefault()とstopPropagation()」が
呼ばれるのですが、jQuery内で独自にイベントを呼び出すループがあり、その条件に
ImmediatePropagationのフラグ判定があります。(一部抜粋)

github,jQuery/src/event.js

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
StupidDog

総合スコア263

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

stack-overflow

2016/02/13 16:11

回答ありがとうございます。 event.stopImmediatePropagation()ですか! はじめて知りましたが、求めていた動作が得られました。 感謝いたします。
miyabi-sun

2016/02/18 03:02

event.stopImmediatePropagation知らなかったです。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問