Javascriptで押すと開閉するメニューを作ろうと思い、下記のようなコードを書きました。

ところが、どんどん押していくうちに、押した所と違う内容が開いてしまいます。

-- 希望の動作 --
・メニューを押すとその真下に内容が表示され、もう一回同じメニューを押すと内容が非表示になる
・内容を表示した状態で、別のメニューを押すと、内容を自動で非表示にし、押したメニューの内容を表示する
・内容が表示されている状態で、なにもないところをクリックしたら、内容を閉じる

一番良い回答を下さった方のみに150P差し上げます。

はてなの文字数制限にひっかかるので別の場所にアップしたコード↓
http://1st.geocities.yahoo.co.jp/gl/barreko00/view/20110610

回答の条件
  • 1人5回まで
  • 登録:
  • 終了:2011/06/11 12:02:28
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:HowAreYou No.2

回答回数91ベストアンサー獲得回数17

ポイント75pt

同時に二つ開くことはないと判断しました。


function $stl(id) { return document.getElementById(id).style ; }

var active = '' ;

function show(id) {
  if (active) $stl(active+'-menu').display = 'none' ;

  if (id != active) {
    $stl(id+'-menu').display = 'block' ;
    active = id ;
  }
  else active = '' ;
}

何もないところをクリックはよく分からなかったので保留。

id:muggy0812

確認しました。「何もないところ・・」以外は、希望通りの動作です。

何もないところをクリック、というのは「画面のメニュー以外部分をクリックしたら・・・」という意味で、ポップアップを使っているサイトではよくある実装のアレです。

2011/06/10 16:06:19

その他の回答3件)

id:Lhankor_Mhy No.1

回答回数814ベストアンサー獲得回数232

ポイント25pt

問題はココですね。

  if (active) {
    document.getElementById(active+'-btn').onclick=function(){show(active)};
    var activeElm = document.getElementById(active+'-menu');
    activeElm.style.display='none';
  }

このifが成立した時にonclickはどうなるかというと、

function(){show(active)}

です。

おそらく期待している動作は

function(){show('id1')}

などでしょうけれど、そうではないです。

 

ちなみに、hideの場合も

function(){show('id1')}

ではなく

function(){show(id)}

となっているのですが、こちらはクロージャがあるので大丈夫ですね。

 

 

 

正しく動作するコードが必要でしたら追記いたします。

 

 

 

追記:

 

document.getElementById(active+'-btn').onclick=function(){show(active)};

↑の部分を以下のように変更。

document.getElementById(active+'-btn').onclick=(
  function(active){
    return function(){show(active)}
  }
)(active);

一応、動作確認してます。ご想定の動作と違うようでしたらご指摘ください。

 

自分もアマグラマなので正確には説明できないのですが、JavaScriptの場合、functionごとにスコープがあるような感じです。

ですので、activeはグローバル変数として書き換えられてしまい正しく動作せず、idはfunctionの中での値を保持しているため正しく動作する、ということみたいです。

変更後のコードでは、引数として取らせてクロージャを作る感じでしょうか。

id:muggy0812

コードお願いできますか。

うーん。これは変数のスコープが良く理解できていないということなのか・・・

クロージャでちょっと調べてみたら、じっくり読まないと理解できなそうですね

2011/06/10 15:47:05
id:HowAreYou No.2

回答回数91ベストアンサー獲得回数17ここでベストアンサー

ポイント75pt

同時に二つ開くことはないと判断しました。


function $stl(id) { return document.getElementById(id).style ; }

var active = '' ;

function show(id) {
  if (active) $stl(active+'-menu').display = 'none' ;

  if (id != active) {
    $stl(id+'-menu').display = 'block' ;
    active = id ;
  }
  else active = '' ;
}

何もないところをクリックはよく分からなかったので保留。

id:muggy0812

確認しました。「何もないところ・・」以外は、希望通りの動作です。

何もないところをクリック、というのは「画面のメニュー以外部分をクリックしたら・・・」という意味で、ポップアップを使っているサイトではよくある実装のアレです。

2011/06/10 16:06:19
id:HowAreYou No.3

回答回数91ベストアンサー獲得回数17

ポイント75pt

ご希望の動作か分かりませんが……

<body onclick="show(null)">

body 、あるいは希望の範囲を div 等でくくって onclick を仕込む。



function $stl(id) { return document.getElementById(id).style ; }

var active = '' ;
var mnClk = false ;

function show(id) {
  if (!id && mnClk) { mnClk = false ; return ; }
  if (active) $stl(active+'-menu').display = 'none' ;

  if (id != active) {
    $stl(id+'-menu').display = 'block' ;
    active = id ;
    mnClk = true ;
  }
  else active = '' ;
}
id:Cherenkov No.4

回答回数1504ベストアンサー獲得回数493

ポイント25pt

jQueryで実装してみました。

onclick="show('id1')"のように要素毎に書くのは面倒だと思うので消しました。

表示/非表示はtoggle()に任せるのが楽です。

id1-btn、id1-menuのようにidで管理するのではなく、classとdivなどで階層を作ってボタンとメニューを管理すれば、要素を追加するだけで(id1~とも書かずに)すっきりと書けます。


demo: http://jsfiddle.net/L8JpD/


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script>
//http://q.hatena.ne.jp/1307682576
$(function() {
  $('[id^="id"][id$="-btn"]').click(function() {
    var self = $(this);
    var ac = $('.active');
    if (ac.length) {
      if (ac.attr('id') !== self.attr('id')) {
        ac.trigger('click');
      }
    }
    $('#' + self.attr('id').replace(/btn$/, 'menu')).toggle(200);
    self.toggleClass('active');
  });

  $('body').click(function(e) {
    if ($(e.target).is('*:not([id^="id"][id$="-btn"]):not([id^="id"][id$="-menu"])')) {
      $('.active').trigger('click');
    }
  });
});
</script>
<style>
body {
  width: 2000px;
  height: 2000px;
}
.active {
  background-color: orange;
}
[id^="id"][id$="-menu"] {
  background-color: aliceblue;
}
</style>
</head>
<body>
<span id="id1-btn">[その1]</span><br />
<div id="id1-menu" style="display:none;cursor:pointer">--内容1--</div>
<span id="id2-btn">[その2]</span><br />
<div id="id2-menu" style="display:none;cursor:pointer">--内容2--</div>
<span id="id3-btn">[その3]</span><br />
<div id="id3-menu" style="display:none;cursor:pointer">--内容3--</div>
<span id="id4-btn">[その4]</span><br />
<div id="id4-menu" style="display:none;cursor:pointer">--内容4--</div>
</body>
</html>


参考までに jQuery UI - Accordion というのもあります。


質問の際にコードの断片を貼るのではなく、回答者がコピペですぐ確認できるコードのほうが、問題を共有しやすいですよ。

のようなサービスを積極的に利用しましょう。

あと回答者に対して、サンプルの提示を求めれば確認が楽になるかと。

id:muggy0812

参考になるアドバイスありがとうございます。

このデモに使っているサイトいいですね

言っておけばよかったのですが、現在prototype.jsを使っており

jqueryは競合が恐いので利用を避けています。

2011/06/11 11:58:32
  • id:muggy0812
    どの回答もよかったので非常に迷いましたが、いち早く
    動作するコードを提示していただいたHowAreYouさんに
    150Pとさせて頂きます。

    質問に答えていただいた皆様、ありがとうございまいた。
  • id:Cherenkov
    prototype.jsならtoggleが使えるじゃないですか…
    http://api.prototypejs.org/dom/Element/toggle/
  • id:muggy0812
    何分プログラムにうといもので、特にJavascriptは全然なんですね。
    prototypeとjqueryの特徴もよくわかってなく、都度ググってサンプルを見つけこれでいいかな?
    っていうスクリプトを真似て書いている、という手探り状態です

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません