人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

Javascriptで押すと開閉するメニューを作ろうと思い、下記のようなコードを書きました。
ところが、どんどん押していくうちに、押した所と違う内容が開いてしまいます。

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

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

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


●質問者: muggy0812
●カテゴリ:ウェブ制作
✍キーワード:JavaScript はてな クリック コード 差し
○ 状態 :終了
└ 回答数 : 4/4件

▽最新の回答へ

1 ● Lhankor_Mhy
●25ポイント

問題はココですね。

 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の中での値を保持しているため正しく動作する、ということみたいです。

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

◎質問者からの返答

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

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

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


2 ● HowAreYou
●75ポイント ベストアンサー

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


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 = '' ;
}

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

◎質問者からの返答

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

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


3 ● HowAreYou
●75ポイント

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

<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 = '' ;
}

4 ● Cherenkov
●25ポイント

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 というのもあります。


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

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

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

◎質問者からの返答

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

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

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

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

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ