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

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

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

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

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

Q&A

解決済

4回答

13335閲覧

.slideToggle()が勝手に何度も実行される状態を解消したい

nuttstock

総合スコア19

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

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

2グッド

1クリップ

投稿2016/03/10 00:31

初めましてこんにちは。
javascriptの.slideToggle()が何度も実行されて、開閉が何度も起こる事態について質問させていただきます。
これは、何度もクリックしたときにその回数分開くのをstop()で止めたいという種類のものではありません。

したいことは、サイドナビゲーションを、userのスマホやタブレットとそれ以外によって条件分岐をし、更に画面サイズ(800px)で条件分岐をします。800px未満の場合は、サイドナビゲーションのSIDENAVI以外の部分を閉じた状態にし、SIDENAVI の部分をクリックしたとき、それ以下のリスト部分を開閉させ、800p以上の場合は、全部開いた状態にします。
閉じた状態が初期状態の場合(800px未満)は、.oc-markの文字を、リストが閉じているときは"開"、開いているときは"閉"にします。開いた状態が初期状態の場合(800px以上)は、クリックで開閉させず、"開"も"閉"も表示させません。(開、閉の非表示は、javascriptを使わず、cssで実装しています。)また、スマホが回転されたときやPCでブラウザの幅をリサイズされたときに、画面サイズを再度取得し、それに合わせてコンテンツを表示しています。

問題は現在の状態だと、
スマホの場合、回転させていない場合は意図通りクリックで開閉してくれるのですが、一度画面を回転させると、開いて閉じる、という動作になり、**二度目回転させると、開いて閉じて開く、**という状態になります。さらに何度も回転させていると、一度のクリックで、その回転させた回数分開閉を繰り返します。
PCの場合は、どこかの時点で画面をリサイズし、800px未満に一度なると、そのあと画面を広げて800px以上にして、サイドメニューが常に表示される状態になってほしくても、開いた状態にはなるのですが、クリックで開閉されてしまいます。さらにこちらも一度のクリックで何度も開閉を繰り返すようになります。

javascriptのコメントアウトの部分は、この何度も開閉する状態を回避するために、回転やリサイズされた際に強制的にリロードさせていたのですが、今回、ビデオやオーディオといったコンテンツを配信することにしましたので、スマホの回転や画面サイズ変更のたびにリロードしていると、再生していたコンテンツも一緒にリロードされてしまい、継続的に再生できない事態になりますので、リロードさせることができなくなりました。

初心者の域を出ておらず、効率的なコードの組み方がわからないため、大変恥ずかしい状態ではありますが、解決策がありましたら、ご教授いただきたいと思い、投稿いたしました。
下記が使用しているHTMLとjavascriptです。

どうぞよろしくお願いいたします。

HTML

1<nav class="side-navigation"> 2 <h2> 3 <span class="toggle-text">SIDENAVI 4 <span class="oc-mark">開</span> 5 </span> 6 </h2> 7 <ul class="sidenavi-contents"> 8 <li> 9 <h3>見出し</h3> 10 </li> 11 <li class="sidenavi-list"> 12 <a>りすと1</a> 13 </li> 14 <li class="sidenavi-list"> 15 <a>りすと2</a> 16 </li> 17 <li class="sidenavi-list"> 18 <a>りすと3</a> 19 </li> 20 <li class="sidenavi-list"> 21 <a>りすと4</a> 22 </li> 23 <li class="sidenavi-list"> 24 <a>りすと5</a> 25 </li> 26 </ul> 27</nav>

javascript

1 2$(document).ready(function() { 3 "use strict"; 4 5 var $ua = navigator.userAgent; 6 $(function() { 7 8 if ($ua.indexOf('iPhone') > 0 || $ua.indexOf('iPad') > 0 || $ua.indexOf('iPod') > 0 || $ua.indexOf('android') > 0 || $ua.indexOf('BlackBerry') > 0 || $ua.indexOf('windows Phone') > 0 || $ua.indexOf('NOKIA') > 0 || /Mobile.*Firefox/.test($ua)) { 9 10 $(window).on("load orientationchange", Fitsizem); 11 12 function Fitsizem() { 13 var _width = $(window).width(); 14 15 if (_width <= 800) { 16 17 $(".side-navigation .sidenavi-contents").hide(); 18 var flg = "close"; 19 20 $(".side-navigation h2").click(function() { 21 var snavtglpart = $(".side-navigation .sidenavi-contents"); 22 var sntexchange = $(".side-navigation h2 .toggle-text .oc-mark"); 23 24 snavtglpart.slideToggle(); 25 26 if (flg === "close") { 27 sntexchange.text("閉"); 28 flg = "open"; 29 } else { 30 sntexchange.text("開"); 31 flg = "close"; 32 } 33 }); 34 35 } else { 36 $(".side-navigation .sidenavi-contents").show(); 37 } 38 } 39 /* 40 var timer; 41 $(window).on("orientationchange", function() { 42 clearTimeout(timer); 43 timer = setTimeout(function() { 44 location.reload(true); 45 }, 100); 46 }); 47 */ 48 49 } else { 50 51 $(window).on("load resize", Fitsizepc); 52 53 function Fitsizepc() { 54 var _width = $(window).width(); 55 56 if (_width <= 800) { 57 58 $(".side-navigation .sidenavi-contents").hide(); 59 var flg = "close"; 60 61 $(".side-navigation h2").click(function() { 62 var snavtglpart = $(".side-navigation .sidenavi-contents"); 63 var sntexchange = $(".side-navigation h2 .toggle-text .oc-mark"); 64 65 snavtglpart.slideToggle(); 66 67 if (flg === "close") { 68 sntexchange.text("閉"); 69 flg = "open"; 70 } else { 71 sntexchange.text("開"); 72 flg = "close"; 73 } 74 }); 75 76 } else { 77 $(".side-navigation .sidenavi-contents").show(); 78 } 79 /* 80 var timer = false; 81 $(window).resize(function() { 82 if (timer !== false) { 83 clearTimeout(timer); 84 } 85 timer = setTimeout(function() { 86 location.reload(true); 87 }, 200); 88 }); 89 */ 90 91 } 92 } 93 }); 94});
misa, goligo👍を押しています

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

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

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

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

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

guest

回答4

0

イベントの設定が実行されるタイミングをロード時の1回だけにしましょう。
何度も繰り返し実行される関数内に、クリックイベントの設定を書くとご質問されているような意図しない動作に当然なります。

それと、下記は同じ意味です。なので、入れ子で使われていますが内側の関数は不要です。
プログラム的には、ご自身が書いたとおりに動いています。いろいろと見直すといいと思います。

javascript

1$document.ready(function() { 2 // 3}); 4 5$(function() { 6 // 7}); 8

投稿2016/03/10 05:01

編集2016/03/10 05:05
yamato_hikawa

総合スコア2092

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

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

nuttstock

2016/03/10 08:15

ご回答いただき、ありがとうございます。 アドバイスありがとうございます。 もう一度考えてみます。
guest

0

あまり確りと見ていませんが、
$("").click(function() {}は呼ばれるたびにイベントリスナーが追加されていったと思います。(呼ばれるたびに同じイベントが追加されていきます)
この場合、resize orientation loadのたびに追加されているのかと考えられます。

投稿2016/03/10 03:46

S-Rui_Sato

総合スコア27

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

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

nuttstock

2016/03/10 08:17

ご回答いただき、ありがとうございます。 皆さんにアドバイスいただいても、あまりピンとこず、やはりきちんとしたjavascriptの素地がないことが大きな原因だと思い知りました。 もう一度考えてみます。
guest

0

ベストアンサー

時間がなくて書き殴りの未検証コードですが、こういうことがやりたいのかな、というのを整理して書いてみました。
元々のコードだと、ShunsukeIzumi さんが指摘されているように、向きが変わったりリサイズしたときに起動されるイベントハンドラの中で onClick イベントのハンドラを追加しているので、同じイベントハンドラが何度も追加されていってしまっています。document の ready イベントで一回だけ起動されるイベントハンドラの中で一度だけ onClick イベントのハンドラを設定するようにするといいのではないでしょうか。

javascript

1$(document).ready(function() { 2 "use strict"; 3 var isMobile = (function($ua) { 4 return ($ua.indexOf('iPhone') > 0 || $ua.indexOf('iPad') > 0 || 5 $ua.indexOf('iPod') > 0 || $ua.indexOf('android') > 0 || 6 $ua.indexOf('BlackBerry') > 0 || 7 $ua.indexOf('windows Phone') > 0 || $ua.indexOf('NOKIA') > 0 || 8 /Mobile.*Firefox/.test($ua)); 9 })(navigator.userAgent); 10 11 var $content = $(".side-navigation .sidenavi-contents");] 12 var $sntexchange = $(".side-navigation h2 .toggle-text .oc-mark"); 13 $(".side-navigation h2").click(function() { 14 if ($content.is(':visible')) { 15 $content.slideUp(); 16 sntexchange.text("開"); 17 } else { 18 $content.slideUp(); 19 $sntexchange.text("閉"); 20 } 21 }); 22 function fitsize() { 23 if ($(window).width() <= 800) { 24 $content.hide(); 25 } else { 26 $content.show(); 27 } 28 } 29 $(window) 30 .on("load orientationchange", fitsize) 31 .on("load resize", fitsize) 32 ; 33});

なんか整理して書いてみたら isMobile 変数を使わない気がしてきました ...。


追記:修正版

またもや書きっぱなし状態で時間切れになってしまったので、解説はまたあとで ...。

javascript

1$.fn.mynavi = function() { 2 var isMobile = (function($ua) { 3 return ($ua.indexOf('iPhone') > 0 || $ua.indexOf('iPad') > 0 || 4 $ua.indexOf('iPod') > 0 || $ua.indexOf('android') > 0 || 5 $ua.indexOf('BlackBerry') > 0 || 6 $ua.indexOf('windows Phone') > 0 || $ua.indexOf('NOKIA') > 0 || 7 /Mobile.*Firefox/.test($ua)); 8 })(navigator.userAgent); 9 10 var $navi = $(this); 11 var $content = $navi.children('.sidenavi-contents'); 12 var $button = $navi.children('h2'); 13 var $toggleText = $button.children('.toggle-text'); 14 var $sntexchange = $toggleText.children('.oc-mark'); 15 $button.click(function() { 16 // 800px 以上の場合、h2 を隠しているのでクリックできないので呼ばれないはずだけど 17 // 万が一呼ばれたら何もせずに return 18 if ($navi.data('wide')) return false; 19 if ($content.is(':visible')) { 20 $content.slideUp(); 21 $sntexchange.text("開"); 22 } else { 23 $content.slideDown(); 24 $sntexchange.text("閉"); 25 } 26 return false; 27 }); 28 function fitsize() { 29 if ($(window).width() <= 800) { 30 // $content.hide(); // 800px 未満になったときに急にリストが消えるのが変かなと思い 31 $toggleText.show(); 32 $navi.data('wide', false); // 800px 未満だったことを記憶 33 } else { 34 $content.show(); 35 $toggleText.hide(); // h2 を隠し、クリックさせないようにする 36 $navi.data('wide', true); // 800px 以上だったことを記憶 37 } 38 } 39 $(window) 40 .on("load orientationchange", fitsize) 41 .on("load resize", fitsize) 42 ; 43 fitsize(); // 初期化 44 if ($content.is(':visible')) { // もし最初開いたときに開いていたら「閉」に直す 45 $sntexchange.text("閉"); 46 } 47}; 48$(function() { 49 $('.side-navigation').mynavi(); 50});

再度追記

まず、質問のタグに「jQuery」もつけるとよかったかな、と思いました。

他の方も書かれていますし、繰り返しになりますが、「最初に一回だけ実行すること」と「イベントが発生したときに毎回実行すること」とを意識的にわけて考えてコードを書くとよいかな、と思いました。onClick に対する処理(イベントハンドラ)は毎回実行されるわけですが、その登録は一回だけすればいいわけです。

それと、.slideToggle() は、「ある要素が閉じていたら開く、開いていたら閉じる」というときに限定して使い、「ある要素が閉じていたら開きつつ関連する他の要素の状態を変え、開いていたら閉じつつ関連するほかの要素の状態も変える」というケースでは使わないほうがよいと思います。というのは、.slideToggle() で表示状態が反転するわけですが、関連する要素の状態が別のところで変更されている場合に整合性が取れなくなる可能性があるからです。それよりは is(':visible') もしくは is(':hidden') で表示状態をチェックし、.slideUp().slideDown() で明示的に非表示、表示を切り替えるといいと思います。

それと、$.fn.mynavi = function() { ... } というのは jQuery のプラグインの定義です。必ずしも プラグインにしなくてもよいのですが、そのほうが見通しがよくなるのと、他で再利用しやすいので私は使うことが多いです。

初期化の部分はもっとすっきり書けそうですが、あまり追求しませんでした。

もし、望んでいる動作と違うような場合はご指摘ください。

投稿2016/03/10 01:35

編集2016/03/10 21:28
unau

総合スコア2468

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

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

nuttstock

2016/03/10 02:06

ご回答いただき、ありがとうございます。 僭越ではありますが、このコードの場合、800px以上の時にクリックでリスト部分が閉じてしまい、その後開きません。800px以下の場合、リストは閉じていますが、クリックで開きません。.oc-markの部分だけ、クリックで一度だけ変更されるだけで、その後はクリックも無効です。 11行目の ] を削除し、 16行目の sntexchange.text("開"); の頭に$を加えて実行した結果、 上記の結果になりました。 また何か解決方法が浮かびましたら、ご回答いただけると幸いです。 よろしくお願いいたします。
unau

2016/03/10 07:58 編集

外仕事から戻ってきたんでこれから修正版を作ってみます。
nuttstock

2016/03/10 20:17

ありがとうございました。 作っていただいたスクリプトでおかしな挙動が見当たらなくなりました。 貴重な時間を割いて問題に付き合っていただき、誠にありがとうございます。 アドバイスもたくさんいただき、今後に役立てたいと思います。 >>質問のタグに「jQuery」もつけるとよかったかな、と思いました。 すっかり抜けてしまっていました。今後、そのようにいたします。 ありがとうございました。
guest

0

リサイズする度にイベントが設定されているためではないでしょうか。

投稿2016/03/10 00:52

orange0190

総合スコア1698

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

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

nuttstock

2016/03/10 02:08

ご回答いただき、ありがとうございます。 継続的によく考えてみます。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問