- ベストアンサー
縦型アコーディオンメニューの不具合について
- 縦型アコーディオンメニューを作成しましたが、一部の不具合が発生しています。
- 一瞬だけマウスをメニュー項目に持っていくと、選択したメニューが開いたままになってしまいます。
- この不具合は修正することができないのでしょうか?困っています。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
>hide()を使ったやり方ではメニューの開閉はスムーズになったのですが、 >今度はメニュー内の項目が選べませんでした。 メニューとサブメニューの両方から2度、showPopupが呼び出されているので、自分を閉じてはダメでしたね。(失礼しました) 自分は閉じないことにしてあげればOKですが、即座に閉じるためサブメニューから次のメニューに移るときに、カーソル位置が相対的に移動してしまう(表示が変化するので)という問題が残るかも知れません。 (↓の呼び出し側はhide(1);、 hide(2);、 hide(3)のようにしてください) var menu_1 = { timeout : null,showPopup : function(){ hide(1); clearTimeout(this.timeout); if($('menu_1').style.display == 'none'){ this.timeout = setTimeout(function(){ new Effect.BlindDown('menu_1', {duration:.3, fps:40}) },400);}}, hidePopup : function(){ if($('menu_1').style.display == 'none'){ clearTimeout(this.timeout); }else{this.timeout = setTimeout(function(){ new Effect.BlindUp('menu_1',{ duration:.3, fps:40})},350); }}} ~~~以下省略~~~~ function hide(n){ for (var i=1; i<=3; i++){ if (i!=n) { clearTimeout(eval('menu_' + i + '.timeout')); $('menu_' + i).style.display = 'none'; }}} 直接書くほうはご提示の内容で自分を閉じないようにはなっていますが、showPopupの中にいれないとうまくないでしょう。 中にいれれば、↑と同じものになります。 Effectの動作を途中でキャンセルする方法がわかれば簡単なんですが、なんせeffects.jsはまったく知りませんので… jQeryのeffectなどだと、途中でキャンセルできるみたいなんですけれどね。
その他の回答 (3)
- fujillin
- ベストアンサー率61% (1594/2576)
#1、#2です。 >通常は、thisの中身はどうなっているのでしょうか? thisは特定のものを指してはいないので、使用される環境下で意味するものが変わってきます。 一般的にはこんな感じ(ぴったりのサイトが見つかりませんでした) http://www.fladdict.net/blog-jp/archives/2005/09/javascript_acti.php http://d.hatena.ne.jp/uokumura/20090505/1241471253 ちょっと特殊な条件下だと http://d.hatena.ne.jp/ynakajima/20090207/p1 http://hisasann.com/housetect/2008/02/javascriptthis.html >var test = this; >alert(test); >上記をアラートで出そうと思ったのですが、アラーとでは、 >object Object >と出てしまって、中身が分かりません。 どこに記載したのか不明なので、thisが何を意味しているのかわかりかねますが… 表示される内容から、一応、thisがobjectであることはわかりますよね? this.toSorce() では、toSorce()というfunctionが定義されていないので、未定義というエラーになっていると想像します。 (未定義のプロパティだとundefinedになります) 例えば、もとのコードで clearTimeout(this.timeout); となっているときの、thisは変数オブジェクト自身を指しています。 それなので、 this.timeout としたときは、その2行上で初期定義しているtimeoutを意味することになります。 3行下では、これに this.timeout = setTime(~~) としているので、 setTimeoutのIDとして利用していることがわかります。 試みにshowPopupの中で alert(this.timeout); としてみれば、最初はnull(=初期設定値)が、以後は数字(=ID)が表示されるはずですので、その意味するところを理解してください。 また、alert(this.showPopup); などとしてみると function()以下の自分自身の関数がそのまま返されることがわかると思います。 …というようなものなので、 >htmlをかまっていて、showPopup内の clearTimeout(this.timeout); >のthisが壊れてしまっています。 ということは起きてはいないと思うのですが…? ------------------------------------------------- いまさらではありますが、#1、#2の対処法は中身を全部理解した上での方法ではないので、あまりお勧めはいたしません。 というか、もともとの書き方(同じ内容のスクリプトを、メニューの個数分並べる方法)自体があまり勧められる方法とは言えないように思いますし、さらにHTMLソース側でもそれぞれのリンクタグに <a href="javascript:void(0);" onmouseout="menu_2.hidePopup()" onmouseover="menu_2.showPopup()">menu #02</a> みたいなのを書き連ねるのも洗練された方法とは思えません。 (ソースも見やすくなくなりますし) #2の後半にライブラリの利用方法も書いておきましたが、機能的に問題なければそのようなものを利用なさるのも一法かと思いますが…? HTMLでの設置方法(記述法)などを比較してみればわかると思いますが、メニューのHTMLを変更するときのことなどを考えてみれば、スクリプトも同時に修正しなければならないよりも、HTMLだけ修正すればそれですむ方がはるかに簡単ですよね。
お礼
すいません。私の勘違いでおきたものでした。調べてみて解決いたしました。 そして、教えていただいた、 hide()を使ったやり方ではメニューの開閉はスムーズになったのですが、今度はメニュー内の項目が選べませんでした。(開いたらすぐに閉まる状態です。) hidePopup()を使わずに、直接記入するやり方では、メニュー1や2が開いたままになってしまいました。。 一応、#2の後半に記載してあるライブラリの利用方法も考えています。しかし、できる限りは現状のソースを使って行いたいと考えています。 平行して、教えていただいたライブラリーも構築していきます。以下、実際に使っているソースです。 hide()を使ったやり方 var menu_1 = { timeout : null,showPopup : function(){ //clearTimeout(this.timeout); hide(); if($('menu_1').style.display == 'none'){ this.timeout = setTimeout(function(){ new Effect.BlindDown('menu_1', {duration:.3, fps:40}) },400);}}, hidePopup : function(){ if($('menu_1').style.display == 'none'){ clearTimeout(this.timeout); }else{this.timeout = setTimeout(function(){ new Effect.BlindUp('menu_1',{ duration:.3, fps:40})},350); }}} ~~~以下省略~~~~ function hide(){ clearTimeout(menu_1.timeout); $('menu_1').style.display='none'; clearTimeout(menu_2.timeout); $('menu_2').style.display='none'; clearTimeout(menu_3.timeout); $('menu_3').style.display='none'; } 直接書くやり方 var menu_1={ timeout : null,showPopup:function(){ clearTimeout(this.timeout); if($('menu_1').style.display=='none'){ this.timeout=setTimeout(function(){ new Effect.BlindDown('menu_1',{ duration:.3,fps:40})},400); }}/*, hidePopup:function(){ if($('menu_1').style.display=='none'){ clearTimeout(this.timeout); }else{this.timeout=setTimeout(function(){ new Effect.BlindUp('menu_1',{duration:.3,fps:40 })},350); }}*/ clearTimeout(menu_2.timeout); $('menu_2').style.display ='none'; clearTimeout(menu_3.timeout); $('menu_3').style.display='none';} 何か分かりますでしょうか? よろしくお願いいたします。
- fujillin
- ベストアンサー率61% (1594/2576)
#1です。 それぞれのメニューがそれぞれ別のコードで制御されているので、例の場合ですと合計3箇所に追加する必要があります。 それで改善できているかは、ご質問の事象がなかなか再現できないので、確認が難しいところですが。 再度実験してみたところ、きちんとメニューは閉じるのですが、開ききらないうちにhidePopup()を実行させてしまうと、どこかで制御変数が狂ってくるらしく、メニューの開く高さがおかしくなってしまう事象が起きることを発見しました。なので、このままではうまくないみたいです。失礼いたしました。 (「prototype」か「effects」の中を良くみてみないとわかりません) いっそのことhidePopup()を使わずに、直接で clearTimeout(menu_2.timeout); $('menu_2').style.display ='none'; にしてしまえば、どうやら大丈夫みたいです。 (ちゃんと調べればもうすこし要領の良い方法があると思いますが…) ↑これだと 2行×2(メニュー)×3(箇所)=12行 も追加しなければならなくてどうにも大仰になってしまいますね。 どうせなら、全部(1~3まで)を閉じるfunctionを作っておいて、各showPopupでそれを呼び出してあげるとか… function hide(){ clearTimeout(menu_1.timeout); $('menu_1').style.display ='none'; clearTimeout(menu_2.timeout); $('menu_2').style.display ='none'; clearTimeout(menu_3.timeout); $('menu_3').style.display ='none'; } を用意しておいて、各showPopupの clearTimeout(this.timeout); の代わりに hide(); とすれば 一応、問題も起きずに動くようです。 (これだと作者の構成イメージをぶちこわしにしているので申し訳ないですが…) ----------------------------------------------------- >それと記載してあったサンプルライブラリの一番下 >これをマウスオーバーにしようとしたらどうしたらいいのでしょうか? 英語は苦手なのでちゃんと読んでませんが… 最初の方の「Examples」の5番目に「accordion with mouseover action」というのがあって、マウスオーバーの例と設置方法が示されています。 http://www.i-marco.nl/weblog/yui-accordion/apple/ その他の設定値(オプション)については、最初のページの「Invocation」の中の「options」で説明されているようですので、内容を確かめてみてください。
お礼
ありがとうございます。 全部(1~3まで)を閉じるfunctionを作っておいて、各showPopupでそれを呼び出してあげる。 というやり方に変えました。 htmlをかまっていて、showPopup内の clearTimeout(this.timeout); のthisが壊れてしまっています。 var test = this; alert(test); 上記をアラートで出そうと思ったのですが、アラーとでは、 object Object と出てしまって、中身が分かりません。 何か確認する方法ってありますか? お願いいたします。
補足
http://javascriptist.net/ref/Object.toSource.html 上記を参考に document.write(this.toSource()); としたのですが、やはりできません。 通常は、thisの中身はどうなっているのでしょうか?
- fujillin
- ベストアンサー率61% (1594/2576)
なかなか再現しませんね。 スライドの動作を遅くしてみたりして試してもみましたが再現しません。 想像では、どこかでmouseoutのイベントがミスされているのだと思うのですが… ◇この手のライブラリはたくさん作られているので、他のものを利用するのも一つの方法ですね。 例えば http://www.dynamicdrive.com/dynamicindex17/ddaccordion.htm http://jquery.bassistance.de/accordion/demo/ http://sandbox.leigeber.com/javascript-accordion/index.html http://docs.jquery.com/UI/Accordion#event-change http://www.i-marco.nl/weblog/yui-accordion/#basic (サンプルがクリック動作になっていたりしますが、確か設定でマウスオーバーにできるはずです) ◇ご提示のものをそのまま修正するなら、 (「prototype」とか「effects」はよく知らないので、対処療法的になってしまいますが…) 表示する時(=showPopup)に念のため他のメニューを閉じるようにしておけば確実になるでしょう。 各メニュー毎にコピーされちゃってるので、煩雑ですが、例えば menu_1だったら、 menu_2.hidePopup(); menu_3.hidePopup(); みたいにしてあげれば、冗長になるけれど確実でしょう。 ついでに、HTMLソースからの呼び出しが引数付になっていますが、実際には利用されていないみたいなので、引数は省略してもいけるみたいですね。(というより、無いほうがソースが見やすい) でも、この方式だとメニューが増えたり変わったりすると、その度にスクリプトも増やしたり修正したりしなければならないので面倒ですね。 メニュー全体を対象とするように再構築してあげれば、使いやすくなりそうだけど…(↑に挙げた、たいていのライブラリはそのようになっています)
お礼
早々のご返答ありがとうございます。 私も開く閉じるスピード遅くしたりしたのですがなかなか・・・。 解答欄に記載してある menu_2.hidePopup(); menu_3.hidePopup();を 以下のaccordion.js内の var menu_1 = { timeout : null, showPopup : function(){ //ここに記載した。 } //内に記載したのですが、他のメニューも開いてしまいます。 あっ!?「みたいにしてあげれば」 と記載してありましたね。 すいません。 具体的にはどういったソースを追記すればいいのでしょうか? それと記載してあったサンプルライブラリの一番下 http://www.i-marco.nl/weblog/yui-accordion/#basic これをマウスオーバーにしようとしたらどうしたらいいのでしょうか? なにぶん素人なので、お手数ですがよろしくお願いいたします。
補足
できました。 根本的な解決ではありませんが、教えていただいた menu_2.hidePopup(); menu_3.hidePopup(); を menu_1のif文の中(以下参照) showPopup : function(){ clearTimeout(this.timeout); if($('menu_1').style.display == 'none'){ menu_2.hidePopup(); menu_3.hidePopup(); this.timeout = setTimeout(function(){new Effect.BlindDown('menu_1', {duration:.3, fps:40})},400); } に記載して、教えていただいた通りmenu_1が開いていたらmenu_2、menu_3は開かないようにする。 これでなんとか大丈夫そうです。 本当にありがとうございました。
お礼
ありがとうございます。 できました。 おかげ様で開いたままの状態も解決され、逆に開かないという問題も解決しました。 同時に2つのメニューが開くこともなくなり、メニューを開くとき、メニューの項目が途中できれてメニュー内の項目が全部見れないという問題も解決でき、ソース自体もきれいになりました。 とても感謝いたします。 私は、5つのメニュー項目を使っているので、以下のようになりました。hideのfor文の繰り返しを教えた頂いた3から5に変更し、使用しました。 var menu_1 = { timeout : null,showPopup : function(){ hide(1); clearTimeout(this.timeout); if($('menu_1').style.display == 'none'){ this.timeout = setTimeout(function(){ new Effect.BlindDown('menu_1', {duration:.3, fps:40}) },400);}}, hidePopup : function(){ if($('menu_1').style.display == 'none'){ clearTimeout(this.timeout); }else{this.timeout = setTimeout(function(){ new Effect.BlindUp('menu_1',{ duration:.3, fps:40})},350); }}} ~~~以下省略~~~~ function hide(n){ for (var i=1; i<=5; i++){ if (i!=n) { clearTimeout(eval('menu_' + i + '.timeout')); $('menu_' + i).style.display = 'none'; }}} 何から何までありがとうございます。