メニューを折りたたむ


■Q&A

■Question

またまたお世話になります。 2002.5.5.Sun No.3398

こんばんわ。またまた質問です。色々試してどうしてもうまくいかないので・・。

<script LANGUAGE=javascript>
<!---
function Menu(M1){
  var span="";
  var menu="";
  if(M1 == 1){
    menu = '<A onClick="Menu(0)">▲</A>';
    span = span + '<LI><A href="http://aaa">aaa</A>';
    span = span + '<LI><A href="http://bbb">bbb</A>';
    span = span + '<LI><A href="http://ccc">ccc</A>';
  }
  if(M1 == 0){
    span = "";
    menu = '<A onClick="Menu(1)">▼</A>';
  }
  document.all.span.innerHTML = span;
  document.all.menu.innerHTML = menu;
}

function Menu2(M2){
  var span2="";
  var menu2=""
  if(M2 == 1){
    menu2 = '<A onClick="Menu2(0)">▲</A>';
    span2 = span2 + '<LI><A href="http://ddd">ddd</A>';
    span2 = span2 + '<LI><A href="http://eee">eee</A>';
    span2 = span2 + '<LI><A href="http://fff">fff</A>';
  }
  if(M2 == 0){
    menu2 = '<A onClick="Menu2(1)">▼</A>';
  }
  document.all.span2.innerHTML = span2;
  document.all.menu2.innerHTML = menu2;
}
//--->
</script>
−− 中略 −−
<UL style="list-style-type:none;"><SPAN id="menu"><A onClick='Menu(1)'>▼</A></SPAN>メニュー1
  <SPAN id="span"></SPAN>
</UL>
<UL style="list-style-type:none;"><SPAN id="menu2"><A onClick='Menu2(1)'>▼</A></SPAN>メニュー2
  <SPAN id="span2"></SPAN>
</UL>

の様なプルダウンメニューを作成したんですが、これだとメニューを増やすとその度に同じ様なJavaScriptの記述をしなければなりません。
これを引数とかをうまく使って、一つのルーチンで出来ないものでしょうか?

# ルーチンって言って良いのかな?JavaScript の function{} を言いたい。
# う〜ん、はがゆい。文字で説明するのはきついなぁ。もっと説明したいけど
# どう書けば良いのか分からない・・・。

■Answer

Re:またまたお世話になります。 ぴろあき 2002.5.5.Wed No.3401

似たような処理を一本化して、できるだけコンパクトに組む…というのは、プログラマ(スクリプタ?)として腕の見せどころですよね。

ちょっと根っこからの組み直しになってしまいますが、こういうのはどうでしょう?
IE4以降の他、Netscape6 にも対応して、さらに Netscape4 や JavaScript がOFFの時はメニューが最初から全部表示されている、いわゆるクロスブラウザ仕様です。
http://faq.creasus.net/02/0505/

># ルーチンって言って良いのかな?JavaScript の function{} を言いたい。
関数(function)を「サブルーチン」、それ以外は「メインルーチン」、総合して「ルーチン」で、大体は通じると思います。

># う〜ん、はがゆい。文字で説明するのはきついなぁ。もっと説明したいけど
># どう書けば良いのか分からない・・・。
自分の少ない少ない回答経験から思いますに、

  • 実際のソースを書く(またはページのURL)
  • どこをどうしたい・ここがわからない等、質問の目的をはっきり書く

この要点を押さえておくと、伝わる可能性がかなり高くなります。

■Thanx

ありがとうございます。 2002.5.7.Tue No.3411

上記を参考にして自分のものにしてみます。
ちょっと、今日はもう時間がないので明日にでも早速。
結果はのちほどご報告いたします。

いや〜、勉強になるなぁ。


実行結果

▼をクリックするとメニューが開きます。

■この手法での実行結果

innerHTML 式での実行結果

  • メニュー1  
  • メニュー2  

まったく同じ動作をしてるのがわかるかと思います。
それでいてソースも簡潔で、しかもクロスブラウザ対応ですから、大変お得です。

なお、マージンの違いはタグ構造によるものですので、支障がある場合はスタイルシートなどで調整してください。


■スクリプトの概要

元のスクリプトはクリックされたら innerHTML で中身を書き換えるようになっていますが、このサンプルではスタイルシートで中身を「非表示」にしておき、クリックされたら「表示」に切り替えるようにしています。

また、非対応ブラウザでは「表示」のままにしておくことで、JavaScript がOFFでも支障がなくなります。


■このスクリプトの対応ブラウザ

IE4以降・Netscape6以降・Opera7以降
動作します。
その他
動作しませんが、メニューが最初から表示されています。

■ソース

■下準備

var brwCheck = false;
if ((document.all && document.selection) ||
    (navigator.product && navigator.product == "Gecko") ||
    (window.opera && document.createEvent)) {
  brwCheck = true;
  document.write("<style type='text/css'> .none { display:none;} <\/style>");
}

■HTMLのソース

<ul style="list-style-type:none;">
  <li><span onclick="clickMenu('menuList1', this)">▼</span>メニュー1</li>
  <li><div class="none" id="menuList1">
    <a href="http://aaa">aaa</a><br>
    <a href="http://bbb">bbb</a><br>
    <a href="http://ccc">ccc</a>
  <li></div>
</ul>

<ul style="list-style-type:none;">
  <li><span onclick="clickMenu('menuList2', this)">▼</span>メニュー2</li>
  <li><div class="none" id="menuList2">
    <a href="http://eee">eee</a><br>
    <a href="http://fff">fff</a>
  <li></div>
</ul>

■統一関数

function clickMenu(idName, spanObj) {
  if (brwCheck) {
    with (document.all ? document.all(idName).style : document.getElementById(idName).style) {
      if (display == "block") {
        display = "none";
        spanObj.innerHTML = "▲";
      } else {
        display = "block";
        spanObj.innerHTML = "▼";
      }
    }
  }
}
function clickMenu(idName, spanObj) {
  if (brwCheck) {                            //ブラウザ判定
    var obj;                                 //<div>オブジェクトを格納する一時変数
    if (document.all) {
      obj = document.all(idName);            //IE4以降の時
    } else {
      obj = document.getElementById(idName); //Netscape6の時(IE5以降はこの方法でも可)
    }
    if (obj.style.display == "block") {      //オブジェクトのCSSが「表示」になっていたら
      obj.style.display = "none";            //「非表示」にする
      spanObj.innerHTML = "▲";              //<span>の中身を▲に書き換える
    } else {
      obj.style.display = "block";           //「表示」にする
      spanObj.innerHTML = "▼";
    }
  }
}

■document.all の色々

document.all でオブジェクトIDへアクセスするには、

の3通りの構文があります。プロパティ型は処理速度が速い反面、融通が利きません。メソッド型と連想配列型は、文字列をコードへ変換する処理が加わりますが、ID名に変数を使えるのは絶大なメリットです。
いずれも、負荷の高いスクリプトでもない限り、速度にたいした差はありません。通常はメソッド型か連想配列型の使用が良いかと思います。


■おまけ

関数化について


■追記:2003.8.19.Tue

このページの質疑応答部分も「タイトルをクリックすると記事を表示する」というものですが、このサンプルより遥かに高度な手法を使っています。(該当ソースのどこを見ても onclick はない)
でも「クリックするたびに displaynoneblock で切り替える」という仕組み自体は同じです。


■ このページについて

このページは、質疑応答掲示板で使用した回答の残骸です。検索エンジンに捕まったりリサイクルしたりすることもあろうので、なんとなく残してあります。広く公開しているページではありませんが、第3者の閲覧を禁止するものでもありません。
恒久的なURIは保証できないものの、よほどのことがない限り削除されることはないでしょう。心配性な人はページの保存をおすすめします。(IEは mht 形式でないと保存できないかも)