【JAVASCRIPT】

よく記事の折りたたみのJAVASCRIPTがありますが、以下サイトのような「全てのレスの本文を表示」「全てのレスの本文を隠す」を備え、個別にも折りたたみできるようなスクリプトの具体的なソースをどなたかご教授願えませんか?あまりprototypeなどは使用したくありません。あと複数のブラウザで動作するものを望んでいます。次ページに移動したり、そのページに戻ったりしてもその状態がキープされればなお良いのですが・・・。完璧な回答の方には高ポイント差し上げます。よろしくお願いします。

http://komachi.yomiuri.co.jp/t/2010/0601/319856.htm?g=04

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

回答3件)

id:koriki-kozou No.1

回答回数480ベストアンサー獲得回数79

ポイント300pt

前回質問 http://q.hatena.ne.jp/1275454037 のコメント欄での付加情報を確認のため書き加えておきます

・ブログやCMSなどへの適用ではなく通常のページに採用するとの事

・javascriptについてはあまり判らないとの事

・状態保存は今回質問で新たに追加された仕様


ブログやCMSを使っている場合は構成要素が特定できるためプログラムしやすいのだが、通常ページとなると構成から考えなければならないという大きな問題がある

まずは、前回質問向けに説明用として作っておいたものがあるので比較して欲しい

(状態の保存についてはentryにidを付加するところまでしか行っていない。)


(1)最低限の機能に絞った例

<html>
<head>
    <meta http-equiv="content-style-type" content="text/css">
    <meta http-equiv="content-script-type" content="text/javascript">
    <style>
    <!--
        /* デフォルト非表示の場合の例 */
        div.entryBody { display:none; }
    //-->
    </style>
    <script type="text/javascript">
    <!--
        function visibleControl(e1, e2) {
            var c = (e1.className == 'enable' ? 'block' : 'none'); // 表示か非表示かを設定
            if (e2 != '') { // 特定のエントリーが指示されている場合の処理
                document.getElementById(e2).style.display = c; // 
            } else { // 指示がない場合の処理
                // getElementsByClass を使うと楽なのだが、より多くのブラウザに対応させるため TagName を採用
                for each(var d in document.getElementsByTagName('div')) { // すべてのdivタグをスキャン
                    if (d.className == 'entryBody') { // 'entryBody'クラスだけを対象にする
                        d.style.display = c;
                    }
                }
            }
        }
    //-->
    </script>
</head>
<body>
<div id="header">ヘッダー</div>
<div class="menu">メニュー</div>
<a href="#" class="enable" onclick="visibleControl(this, '')">▼全エントリー表示する</a>
<a href="#" class="disable" onclick="visibleControl(this, '')">▲全エントリー隠す</a>
<hr />
<a href="#" class="enable" onclick="visibleControl(this, 'e1')">▼エントリー1を開く</a>
<a href="#" class="disable" onclick="visibleControl(this, 'e1')">▲エントリー1を隠す</a>
<div id="e1" class="entryBody">エントリー1の本文だよ</div>
<hr />
<a href="#" class="enable" onclick="visibleControl(this, 'e2')">▼エントリー2を開く</a>
<a href="#" class="disable" onclick="visibleControl(this, 'e2')">▲エントリー2を隠す</a>
<div id="e2" class="entryBody">エントリー2の本文だよ</div>
<hr />
<div>その他もろもろ</div>
<hr />
<div id="footer">フッター</div>
</body>
</html>

(2)見栄えを少しだけ整えた例

<html>
<head>
    <meta http-equiv="content-style-type" content="text/css">
    <meta http-equiv="content-script-type" content="text/javascript">
    <style>
    <!--
        /* デフォルト非表示 */
        div.entryBody { display:none; }
        /* 今回はAタグを使ったのでボタン風にアレンジ */
        a.enable, a.disable { margin:3px; padding:2px; border:2px outset #cccccc; background:#cccccc; text-decoration:none; color:#000000; }
    //-->
    </style>
    <script type="text/javascript">
    <!--
        function visibleControl(e) {
            //
            // 表示か非表示かを設定
            var c = (e.className == 'disable' ? 'none': 'block');
            //
            // 対象とする範囲の決定
            var p = e.parentNode; // 親ノードを対象とする
            if (p.className == 'entryTitle' || p.className == 'entryBody') { // エントリー内のボタンの場合の対象範囲
                p = p.parentNode; // 親の親ノードを対象とする
            } else { // エントリー外のボタンの場合の対象範囲
                p = document.body; // ボディ全体を対象とする
            }
            //
            // 対象範囲内のdiv要素内をサーチして処理
            for each(var d in p.getElementsByTagName('div')) {
                if (d.className == 'entryBody') { // 本文に対する処理
                    d.style.display = c; // 本文の表示/非表示切り替え
                } else if (d.className == 'entryTitle') { // タイトルに対する処理
                    for each(var a in d.getElementsByTagName('a')) { // 開くボタンを探す
                        if (a.className == 'enable') { // 開くボタンが見つかった場合は表示/非表示を切り替える
                            a.style.display = (e.className == 'enable' ? 'none': 'inline'); // 表示/非表示は押されたボタンによる
                        }
                    }
                }
            }
        }
    //-->
    </script>
</head>
<body>
<div id="header">ヘッダー</div>
<div class="menu">メニュー</div>
<a href="#" class="enable" onclick="visibleControl(this)">全エントリー開く</a>
<a href="#" class="disable" onclick="visibleControl(this)">全エントリー隠す</a>
<hr />
<div id="entry1" class="entry">
    <div class="entryTitle">
        エントリー1のタイトルだよ
        <a href="#" class="enable" onclick="visibleControl(this)">▼エントリー1を開く</a>
    </div>
    <div class="entryBody">
        エントリー1の本文だよ
        <a href="#" class="disable" onclick="visibleControl(this)">▲エントリー1を隠す</a>
    </div>
</div>
<hr />
<div id="entry2" class="entry">
    <div class="entryTitle">
        エントリー2のタイトルだよ
        <a href="#" class="enable" onclick="visibleControl(this)">▼エントリー2を開く</a>
    </div>
    <div class="entryBody">
        エントリー2の本文だよ
        <a href="#" class="disable" onclick="visibleControl(this)">▲エントリー2を隠す</a>
    </div>
</div>
<hr />
<div>その他もろもろ</div>
<hr />
<div id="footer">フッター</div>
</body>
</html>

上記を比較してもらえれば、見た目を整えようとしたり、汎用性を持たせようとすればするほどスクリプトもHTML自体の構成も複雑になってくることがわかると思う

どこで妥協するかが問題になるのだけれど、これ以上は、ベースとなるHTMLの部分がある程度決まってこなければ正直難しい

この先の展開としては、

・ベースとなるHTML部分について上記(1)あるいは(2)にあわせることが出来るのか?

・それとも既に、ある程度出来上がってデザインがあるならば、それを返答してスクリプトのほうをあわせるほうが多分早い

のいずれかを決めて新たな回答(タイミングによるので私が回答するとは限らない)を待ってもらうという形になる

状態保存については、その際に付加すればいい


スクリプトを書いてから言うのもおかしいけど

世の中でCMSが多く採用されている理由が、このような面倒なことを回避したいためだったりするので、オリジナリティにこだわる必要があるかどうか、考え直したほうがいいとは思う

独自スクリプトを理解するよりは、多くの人が使っていて、ある程度完成した物を使うほうが確実だしね

厳密性にこだわるならMovableType、自由度の高さならWordPressあたりを使うと情報多いよ

id:tsuntsuku

ありがとうございます。

ブラウザで見てみたのですがなぜか動きませんでした。

仕事から帰ってきたら再度見てみます。

あと、一応状態保存の件は前回より書いていました。

ベースとなるHTML部分について上記(1)あるいは(2)にあわせること出来ます。

MovableTypeですか・・・。ふーむ・・・。

2010/06/03 08:23:20
id:kent0608 No.2

回答回数220ベストアンサー獲得回数23

ポイント150pt
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>はてなサンプル</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
	$(function(){
		$("#list li div:nth-child(1)").addClass("title");
		$("#list li div:nth-child(2)").addClass("content");
		$(".title").click(function(){
			$(this).next().toggle(300);
		});
		$("#open_all").click(function(){
			$(".content").show(400);
		});
		$("#close_all").click(function(){
			$(".content").hide(400);
		});
	});
</script>
<style type="text/css">
<!--
#list {
	list-style-type: none;
	clear: both;
}
#list li {
	margin-top: 10px;
}
#status {
	float: left;
}
.title {
	background-color: #E1E1E1;
}
.title:hover {
	background-color: #CCC;
}
.content {
	background-color: #F9F9F9;
}
* {
	margin: 0px;
	padding: 0px;
}
#open_all {
	background-color: #FFE79B;
	display: block;
	height: auto;
	width: auto;
	float: left;
	cursor: default;
}
#close_all {
	background-color: #FFE79B;
	display: block;
	height: auto;
	width: auto;
	float: right;
	cursor: default;
}
#open_all:hover {
	background-color: #F90;
}
#close_all:hover {
	background-color: #F90;
}
-->
</style>
</head>

<body>
<div id="open_all">全部開く</div>
<div id="close_all">全部閉じる</div>
<ul id="list">
  <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
    </div>       
</li>
    <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
      </div>
    </li>
    <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
      </div>
    </li>
    <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
      </div>
    </li>
    <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
      </div>
    </li>
</ul>
</body>
</html>

IEとFirefoxで確認してあります。

>あまりprototypeなどは使用したくありません。

javascriptの勉強が目的なので、安易にjQueryやprototypeを使いたくないということでしょうか?

どちらにせよ、クロスブラウザに対応するために上記ライブラリと同じ機能を実装することになります。車輪の再開発はあまり勧められたものではないので、実務においては作業効率向上のためjQueryなどのライブラリを使って下さい。

勉強が目的であればソースコードリーディングがおすすめです。

http://gihyo.jp/dev/feature/01/jquery

id:tsuntsuku

ありがとうございます。

仕事から帰ってきたら詳しく見てみます。

そうですね、jQueryやprototype使うと私には全く仕組みがわからずそして覚えず仕舞いなので(^_^;;)

2010/06/03 08:45:51
id:kent0608 No.3

回答回数220ベストアンサー獲得回数23

ポイント150pt

これ全部開く全部閉じるは両方表示されているのではなく全部開くをクリックしたら全部閉じるに変わるなんていうことも出来るんでしょうか?

答え合わせです。自分で作ったソースと比べてみると、考え方の違いが分かって面白いですよ。


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>はてなサンプル</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
	//変数定義
	var maximum_list_size;
	var current_list_size;
	var duration = 300;
	var LABEL = { CLOSE:"すべて閉じる",
	               OPEN:"すべて開く"
	};
	
	//関数群定義 eはeventの頭文字
	var e = {
				closeAll:
					function(){
					     $(".content").hide(duration);
						 current_list_size = 0;
						 $("#switch").html(LABEL.OPEN);
					},
				openAll:
					function(){
				   	    $(".content").show(duration);
					    current_list_size = maximum_list_size;
				        $("#switch").html(LABEL.CLOSE);
					},
				closeOne: 
					function(t){
					   $(t).next().hide(duration);
					   current_list_size --;
					   if(current_list_size == 0){
						   $("#switch").html(LABEL.OPEN);
					   }

					},
				openOne:
					function(t){
						$(t).next().show(duration);
						current_list_size ++;
						if(current_list_size == maximum_list_size){
							$("#switch").html(LABEL.CLOSE);
						}
						
					}
	}
			 
	//初期化処理	
	$(function(){
		maximum_list_size = $("#list > li").size();
		current_list_size = maximum_list_size;
		
		$("#list li div:nth-child(1)").addClass("title");
		$("#list li div:nth-child(2)").addClass("content");
		
		$(".title").click(function(){
			if($(this).next().css("display") == "none"){
				e.openOne(this);
			}else {
				e.closeOne(this);
			}
		});
		
		$("#switch").click(function(){
			if($("#switch").html() == LABEL.CLOSE){
				e.closeAll();
			}else {
				e.openAll();
			}
		});		
	});
</script>
<style type="text/css">
<!--
#list {
	list-style-type: none;
	clear: both;
}
#list li {
	margin-top: 10px;
}
.title {
	background-color: #E1E1E1;
}
.title:hover {
	background-color: #CCC;
}
.content {
	background-color: #F9F9F9;
}
* {
	margin: 0px;
	padding: 0px;
}
#switch {
	background-color: #FFE79B;
	display: block;
	height: auto;
	width: auto;
	float: left;
	cursor: default;
}

#switch:hover {
	background-color: #F90;
}

-->
</style>
</head>

<body>
<div id="switch">すべて閉じる</div>
<ul id="list">
  <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
    </div>       
</li>
    <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
      </div>
    </li>
    <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
      </div>
    </li>
    <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
      </div>
    </li>
    <li>
    	<div>タイトル</div>
    	<div>内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
        内容内容内容内容内容内容内容内容内容<br />
      </div>
    </li>
</ul>
</body>
</html>
  • id:kent0608
    状態保存について書くのを忘れていました。
    javascriptはクライアントサイドで動くプログラミング言語です。
    つまり、画面の遷移が発生する=サーバサイドに処理が移ると
    すべての状態がリセットされてしまいます。
    もう少し詳しく言うと、どのコンテンツが開いているか閉じているかという情報を何かの
    変数に保存していたとしても、画面が遷移した次の瞬間、すべてなかった事にされます。
    この状態をURLパラメータなどでサーバサイドに送り、サーバ側でそのパラメータを検証し
    次の画面描画に反映するような処理も出来なくはないですが、やらないほうがいいでしょう。

    私が同じような機能を実装するとすれば、非同期通信、つまりAjaxによる
    追加型のページング(Twitterのタイムラインのようなものをイメージしてください)
    にします。これならば画面遷移が発生しないので、どのコンテンツが開いているか閉じているか
    などの状態に変化はありません。
  • id:kent0608
    cssに余計なものが混じってますね
    #status {
    float: left;
    }
    は関係ないので無視してください。
  • id:tsuntsuku
    kent0608さんありがとうございます。
    これ全部開く全部閉じるは両方表示されているのではなく全部開くをクリックしたら全部閉じるに変わるなんていうことも出来るんでしょうか?
  • id:kent0608
    もちろん出来ますよ
    ただここで全部教えてしまうと身につかないので自分で工夫して実践してみて下さい。
    実は一番最初に実装したのはそのパターンでした。cssのstatusはその名残です。
    <div id="status"></div>という記述がどこかにあったということですね。
    これがヒントです。
    jQueryについては下記のリファレンスサイトを活用して下さい。
    http://semooh.jp/jquery/
  • id:kent0608
    加えてこちらも置いておきます。
    Javascriptを使う開発の現場ではFirefox + Firebugが当たり前になっていますので
    是非とも使い方を覚えて下さい。
    http://gihyo.jp/dev/feature/01/firebug/0001
  • id:koriki-kozou
    koriki-kozou 2010/06/03 09:47:38
    >動きません

    ごめんなさい
    for each バージョン(互換性がちょっと低い)を載せてしまいました
    それぞれループ部分を下記に書き直してください
    コメントは消してますが、やってることは一緒
    (1)
    for (var i in document.getElementsByTagName('div')) {
    if (document.getElementsByTagName('div')[i].className == 'entryBody') {
    document.getElementsByTagName('div')[i].style.display = c;
    }
    }
    (2)
    for (var i in p.getElementsByTagName('div')) {
    if (p.getElementsByTagName('div')[i].className == 'entryBody') {
    p.getElementsByTagName('div')[i].style.display = c;
    } else if (p.getElementsByTagName('div')[i].className == 'entryTitle') {
    for (var k in p.getElementsByTagName('div')[i].getElementsByTagName('a')) {
    if (p.getElementsByTagName('div')[i].getElementsByTagName('a')[k].className == 'enable') {
    p.getElementsByTagName('div')[i].getElementsByTagName('a')[k].style.display = (e.className == 'enable' ? 'none': 'inline');
    }
    }
    }
    }
  • id:kent0608
    私の2回目の回答は、上記
    「これ全部開く全部閉じるは両方表示されているのではなく全部開くをクリックしたら全部閉じるに変わるなんていうことも出来るんでしょうか? 」
    の答え合わせ編なので、出来れば自分で一度実装してから開いて下さい。
  • id:edWard
    id:koriki-kozouさんの訂正版は、FirefoxやSafariでは動きませんね。
    IE限定で回答しているのかな?
  • id:Mars
    >両方表示されているのではなく全部開くをクリックしたら全部閉じるに変わる
    個別に閉じるや開くが使えるようにするなら、
    全部開く/閉じるは常に両方表示されていてもいいんじゃないかな?
    一つのボタンが変化した方がカッコウはいいかもしれないけどこの場合は使い勝手が悪くなると思う。

    ライブラリは使いたくないって事だったので面倒なんで回答しませんでしたが、質問の参考URL見てボタン2つに分けないといけないなと真っ先に思いましたよ。
  • id:tsuntsuku
    kent0608さん

    >もちろん出来ますよ
    >ただここで全部教えてしまうと身につかないので自分で工夫して実践してみて下さい。

    出来ましたーーー!合ってるかどうかはわかりませんが、

    function open_all() {
    if(document.getElementById) {
    document.getElementById("naiyou").style.display = "block";
    document.getElementById("open_all").style.display = "none";
    document.getElementById("close_all").style.display = "inline";
    }
    return false;
    }
    function close_all() {
    if(document.getElementById) {
    document.getElementById("naiyou").style.display = "none";
    document.getElementById("open_all").style.display = "inline";
    document.getElementById("close_all").style.display = "none";
    }
    return false;
    }

    こんな感じのを付け加えました。
    本文部分のDIVにidをつけたのですが、ただidって同じのは1ページに1つですよね?classを取得って出来るんでしょうか?
  • id:tsuntsuku
    koriki-kozouさん

    動きました
    ありがとうございます!!
  • id:tsuntsuku
    edWardさん
    FirefoxとChromeで見てみましたが動きましたよ
  • id:tsuntsuku
    Marsさん
    確かに見た目だけにこだわっていてはだめですよね・・・
    反省します
  • id:tsuntsuku
    >本文部分のDIVにidをつけたのですが、ただidって同じのは1ページに1つですよね?classを取得って出来るんでしょうか?

    あ、自己解決しました。ちょっと卑怯ですが、
    document.getElementById("naiyou" + num).style.display
    ってして全部違うIDふればいいんだ
  • id:tsuntsuku
    あれ?
    getElementsByClass
    ってあるのか・・・
  • id:koriki-kozou
    koriki-kozou 2010/06/04 09:16:29
    (a)一部では getElementsByClassName という名で実装されている
    javascriptの実装状況、挙動、htmlなどを解釈してどのように扱っているかなどはブラウザによるので、
    汎用的なものを作ろうとするよりもブラウザ&バージョンを判定して各個撃破していくしかないということになる
    prototypeやjQueryなどを使うとクライアント側の負担が増えるので使いたくはないが時流には逆らえない

    (b)全部開く/全部閉じるボタンを消す処理について次の状態を考えているだろうか?
    1.「全部閉じる」をクリックすると「全部閉じる」ボタンは消えるよね
    2.タイトルをクリックして、いくつかの記事を開くよね
    3.あれ?「全部まとめて閉じたいのに全部閉じるボタンがないぞ!!」
    全部開く/全部閉じるボタンを消すならば、2の状態が発生した時点で「全部閉じる」ボタンを復活させないといけないよね。
    個人的興味の範囲としてはやってみてもいいとは思うけれど、
    ユーザーとして「常に両方表示されていると困ることがあるかどうか?」を考えてみるといいと思う
    記事の場合であれば「開いてもいないのに閉じるボタンがある」と「あれ?今の状態で開いているってこと? 記事本文は無いって事?」なんてことに繋がりそうだけどね
    タイトルをクリックで閉じたり開いたりも便利だけど、見せる側として、見る人が惑わないようにするためには「記事が開かれている状態/閉じている状態」が明確に判るような配慮は必要だと思うよ
  • id:kent0608
    >tsuntsukuさん
    取りあえず実践はされたようですので、私の解答編と比較してどこがどう違うのか
    考察してみて下さい。input→output→inputを繰り返していくうちに自然と自分のものになっていきます。
  • id:tsuntsuku
    koriki-kozouさん
    そうですねぇ・・・
    実際に運営するサイトではprototypeやjQueryなどを使おうと思います。
    ユーザビリティは一番重要ですもんね。ありがとうございます。

    kent0608さん
    私のとぜんぜん違う・・・(^_^;;;)
    ありがとうございます。ちょっとひとつひとつ見ていってみます。

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

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

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

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