dポイントプレゼントキャンペーン実施中!

ソース
http://pastie.org/9724183

jQueryは1.x系を利用していただければ動くと思います。
http://jquery.com/download/
jquery.corner.js
http://jquery.malsup.com/corner/
画像はテキトーに用意していただければ。。。

現在、クリックしたカテゴリの次階層を表示するのに、スライドして表示しています。

例えばカテゴリ2→サブカテゴリ2-2→機能2-2-2まで表示されている状態で、
カテゴリ1をクリックすると、1階層目、2階層目、3階層目とも表示されていた部分が
パッと消えます。
これは、そういう動作にしているので当たり前なのですが・・・。

これを、『開かれていたカテゴリをスライドアップして非表示にさせる』ということを
行いたいです。
クリックされて他のメニューが表示させる際、メニュー間の空けるべき間隔を求めて、
その間隔を保持した上で、不要な間隔分をスライドアップして非表示にさせる。
もしくは、間隔が不足しているのであれば、不足分のみスライドダウンして表示させる。

現在が、一旦間隔用のHTMLを全削除している関係で単純に上記を求めることは
できませんが、加工して何度かトライしてみましたが、以下の点が問題になって
うまく表現させることができません。
 ・複数階層(4階層以上も考慮)があるため、現在開いている間隔を補足できない。
 ・上記も起因して、元々開いていた間隔と今回開くべき間隔が補足できない。

最終的には動的スクリプトによって表現されるメニューであるため、階層や機能がどうなるかは可変です。
そのため、あまり様々なidやclassを乱発させたくなく、必要最小限に留めたいと思っています。
また、動的スクリプト側にて、空けるべき間隔を意識するようなコードは避けたいと考えています。

以上、アドバイスいただけたら幸いです。

A 回答 (8件)

ANo7です。



手元にあるIEがたまたまIE8なのですが、確かにご指摘のようになりますね・・・
(fx、chromeでは予定通りの表示になります)

どうやら、親要素が高さを正しく認識してくれないみたいですね。
スライドが終わってから、親のタブをクリックしてみると、高さを再認識して予定の表示になるかと思います。
…ということから考えると、スライドが終わったところでブラウザに強制的に再レンダリングさせればよさそうに思えます。
slideDownのcallbackを利用すればタイミングをとることは可能ですが、実際にどのような処理をすればいいのかがすぐには思いつきません。(いずれにしろ、何かを変更したふりをするという無意味な処理になると思いますが…)


一方で、最初に提示したfloatでレイアウトする方法ならこのような事象は起きないようですので、これでラッパーの幅を指定するという方法に切り替えるか、上述の再レンダリングを何らかの方法で実現するか、はたまたIEは切り捨てるか(笑)・・・
申し訳ありませんが、すぐにはお役に立てないようです。


ついでながら、HTMLでcategoryクラスなどを指定するのであれば、スクリプトでもそれを利用したほうが簡潔な記述になりますし、可読性もよくなると思います。
(ANo1のスクリプトを再確認願います)
    • good
    • 0
この回答へのお礼

Ctrl+Aなどによって、選択状態にするだけでも再レンダリングされて正常な描画に戻りますね。
せっかくスライドアニメーションをしているのに、ムリヤリレンダリングすることで瞬間的に描画されるのもあれなので、ちょっと悩ましいです・・・。

IE8で動作してくれないと困るので、切るに切れないという現実もあるんですが^^;

教えて頂いた方法を参考にして、『それらしい』ものとしてみたいと思います。
長々とお付き合いいただき、ありがとうございました。

お礼日時:2014/11/25 08:59

ANo6です。



たびたび、すみません。


>メニューの階層は、DBから取得してくる関係上、何階層まで、
>というのがないため、予め幅を決定するということができません
inline要素でレイアウトしていても、liに white-space:nowrap を指定すれば、一応実現できますね。

なんだかスッキリしませんが、現状から修正するならこれが一番簡単かな?
    • good
    • 0
この回答へのお礼

ありがとうございます!!
あのあと色々試行錯誤してて上手くいかなかったので、大変助かります。

それと、最後に申し訳ありませんが、以下のソースで動かしていただけますでしょうか?
何が悪いのか、IE8で、3階層目を展開すると、1階層目の間隔が空きません・・・。
今回入れたwhite-spaceの影響ではなく、いつからこうなってしまったのか分かりません・・・。
http://pastie.org/9733732

例えば
カテゴリ1→サブカテゴリ1-1→機能1-1-1
が表示されるまで展開すると、カテゴリ1とカテゴリ2の間に間隔が空かないんです。
しかし、再度カテゴリ1をクリックすると、なぜか突然間隔が空きます。

Chromeで動かすと正しく動作するので、JavaScriptに誤りがあるというわけではないと思うんですが・・・。
なにかご存知でしたらご教示いただけませんでしょうか。

お礼日時:2014/11/21 13:23

ANo5です。



>私などよりよほど精通されており、大変助かっています。
素人ですので、ほとんどがネットの検索情報です。最近は便利になりましたね。

>メニューの階層は、DBから取得してくる関係上、何階層まで、
>というのがないため、予め幅を決定するということができません。
なるほど。。。対応方法として思いつくのは、
 1)レイアウトの方法を変える(CSSを変える)
 2)スクリプトで最初に階層を調べて、上位要素の幅を設定する
といった方法でしょうか。

1)の方がスマートな解決方法だと思いますし、ANo1に書きましたようにレイアウトと機能を分離した効果でCSSだけを変更することで可能なはずです。(原理的には)
これまでのもので、スクリプトを省いて表示してみるとお分かりになると思いますが、CSSで基本のレイアウトができるように設定しています。(非表示にすればその分をブラウザが自動的に詰めてくれる)

カラム落ちの原因は、ANo5にも書きましたが、inline要素としてレイアウトしている関係で、親要素の幅を超えると改行されてしまうということにあります。(floatも同様です)
divやulのデフォルトの幅は100%(=ウィンドウサイズ)なので、それ以上になるとこの事象が起きてしまいます。

一案として、UL要素を親要素に対してインデントするという考え方でを試してみました。(これならカラム落ちしないはず)
#menu * { padding:0; margin:0; list-style-type:none; }
#menu ul { margin-left:252px; margin-top:-42px }
#menu li a { display:block; width:250px; height:40px; border:1px #C5C5C5 solid; }

上記で、一応実現できますが、少し問題もあるようです。
一つ目はjQueryのslideUp、slideDownはどうやら要素の高さだけを変えているのではないようなので、動きが少し変わって見えてしまうこと。
これを矯正するために、
 margin-top:-42px !important;
にしてmargin-topを固定してしまえば、これまでとほぼ同様の動作になりますが、動きがなめらかでないことが時々あるようです。
多分、slideUp、slideDownを使うとこれが限界かと思いますので、この方法で完全を期すには開閉動作を自作で制御することが必要になるかも知れません。
(ゆっくり考えれば、もっと良いレイアウト方法がありそうな気もします)

もう一つは、相対位置をpx単位で指定しているのですが、ブラウザによってボックスサイズの(実際は枠線の)扱いが異なることです。(IEでご覧になると、ずれるのがわかるかと思います)
これに関しては、情報がたくさんありますので検索してみてください。marginを使わない(さらにボックスを設ける)ようにするかボーダーを使わない(背景画像などで表示する)、あるいはCSSハックなどの対応方法があろうかと思います。


2)の方法は初期設定で全体の横幅を確保しておくという考え方です。
例えば、ご提示の文書構造であれば、li要素のネスト状況を調べることで階層数を取得できるでしょう。
 var nest = 0, items = $("#menu>li");
 while(items.length){
  items = $(">ul>li", items); nest++;
 }
などとすることで、変数nestに階層数を取得できるかと。
(ご提示のサンプルの場合、nest⇒3)
階層数がわかれば、必要な横幅を計算することは可能ですよね。
    • good
    • 0

ANo4です。



CSSはあまりよくわからないのですが…

>・カテゴリや機能の名称を縦中央揃えにしたいです
縦方向の中央配置はHTMLの性格上少々面倒だと思いますが、いくつかの方法が考え出されているようです。
 http://www.bing.com/search?q=CSS+%E4%B8%8A%E4%B8 …
概ね、内部のboxを中央配置する方法か、vertical-alignを利用可能なtable-cell(IEは不可)を利用する方法のようです。
表示する文字列部分を要素として独立させておけば、上記の方法が利用できると思いますが、現状のHTMLそのままだと難しそうな気がします。(少し構成を変えれば可能でしょう。)

文字列が必ず1行で納まるものという条件下であれば、line-heightを指定する方法とかpaddingで調整してしまうとかできそうなので、現状のままでも比較的簡単に実現できるのではないかと想像します。(メニューということなので、この方法でいけるのではないでしょうか?)


>ウィンドウサイズを超えて描画すると階層が崩れるのですが~~
inline要素としてレイアウトしていますので、改行と同じことが起きます。(floatでも同様ですね)
事前に何階層あるかはわかっているはずですので、最上位のUL又はその外側のDIVの幅を指定して、全体の横幅を確保しておくことで予定通りの表示になるものと思います。
    • good
    • 0
この回答へのお礼

度々ありがとうございます。
私などよりよほど精通されており、大変助かっています。

縦中央揃えは、
<span>で<a>をくくり、<span>をdisplay:inline-block、<a>をdisplay:table-cellとすることで実現できました。
IE8でも動作しています。(他はしりませんが・・・。)

メニューの階層は、DBから取得してくる関係上、何階層まで、というのがないため、予め幅を決定するということができません。
(動的スクリプトによりMAX階層を取得してごにょごにょしてwidthを固定指定する、ならいけそうですが。)

やはりそこまでするには、がらっと作り方を変えるしかないでしょうか・・・。

お礼日時:2014/11/19 18:02

ANo3です。



>あまり様々なidやclassを乱発させたくなく~~
必要最小限として id=menu だけを利用して制御するのなら、こんな感じで。

$(function(){
 $("#menu li a").filter(function(i, e){
  return $(e).next("ul").length;
 }).bind("click", function(){
  $("ul", $(this).parent().siblings()).slideUp();
  $(this).next("ul").slideDown();
  return false;
 }).next("ul").hide();
});
    • good
    • 0
この回答へのお礼

わざわざありがとうございます。
全く理解できていませんが、動きました。
徐々に意味を理解していこうと思います。

度々申し訳ありませんが、以下2点についても教えてください。
 ・カテゴリや機能の名称を縦中央揃えにしたいです。
  #menu a { display:inline-block }としている関係上、vertical-alignが効かない
  のですが、どのように設定すると最適でしょうか?

 ・前回教えて頂いた方法で、float指定をやめました。
  #menu ul, #menu li a { display:inline-block; vertical-align:top; }
  ウィンドウサイズを超えて描画すると階層が崩れるのですが、
  スクロールバーが出現するだけで、描画を崩れなくする方法は
  どのようにすると可能でしょうか?

お礼日時:2014/11/19 15:19

ANo2です。



>(ul、liがposition指定でない)と、jquery.corner.jsで角丸がうまく表現されません。
jquery.corner.jsはまったく存じません。
しかしながら、ご提示のサイトにそのような注意事項は見当たらないように思いますが?
(英語はあまりわからないので、ざっと眺めただけですけれど・・・)
そのサイトの表示(DIV要素)もfloat:leftでレイアウトしているようですので、position指定(?)ではないように思いますが・・・(「position指定でないとできない」の意味がよくわかりませんが…)

一方で、ご提示のメニューの場合は、各項目のサイズが同じだと思いますので、わざわざ角丸指定をしなくても、プリミティブに固定サイズの背景で処理してしまえば、どのブラウザでも問題なく表示できるものと想像します。


>$("a.category", $(this).parent().siblings()).css("background-image", "");
>$(this).css("background-image", "url(image/gradation_green_R.jpg)");
アニメーションするわけではないので、回りくどい指定をしなくてもよさそうに思います。
通常、この手の処理をする場合は、
 1)全部をデフォルトにセット(リセット)
 2)対象の要素のみ指定の内容にセット
という手順で行っていると思いますが、この方法のほうが簡潔にすむと思います。

また、スクリプト中にURL等が散在すると必ずしも可読性が良いとは言えないので、CSSであらかじめ設定しておいて、スクリプト内の処理では要素のクラス設定を変えるというふうにしておく方がわかりやすいように思います。


なお、サンプルではレイアウトするのにfloat:leftで行いましたが、float処理はいろいろ面倒なこともあるかもしれませんので、display:inline-blockにしておいて、vertical-align:topで上揃えで表示させる方が良いかもしれません。
    • good
    • 0

ANo1です。



>以下手順のパターンの場合、3階層目がslideUp()されません。
こちらの環境ではそのような事象は発生しないのですが・・・?
 (fx33, IE8 で確認:HTML4.01 strictモード)

HTML body以下のソースはご提示のもののまま、jQueryは手元にあった1.5.2で確認しています。

>現状はクリックされた際に無理矢理<li>を挿入することで、
>上位カテゴリのリスト間に間隔を空けています
No1にも書きましたが、表示されていればそのまま繰り下がるようにCSSでレイアウトをして、スクリプトは表示/非表示(スライド)だけという風に考えた方がすっきりするのでは?
スペース調節のために、不要なLIを入れるとかは、そもそも不自然ではないでしょうか?

>それを実現させ、さらにslideUp()、slideDown()させる方法などはあるでしょうか?
そうなっているつもりなんですけれど・・・?



念のため以下にテストしたソースを載せます。
(jQueryのロード部はアレンジしています)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="ja">
<head><title>test</title>
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">

<style type="text/css">
#menu * { padding:0; margin:0; list-style-type:none; }
#menu ul, #menu li a { display:block; float:left; }
#menu li { overflow:hidden; }
#menu li a { width:250px; height:40px; border:1px #C5C5C5 solid; }
</style>

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery","1");</script>

<script type="text/javascript">
$(function(){
$("#menu ul").hide();
$("#menu a.category").bind("click", function(){
$("ul", $(this).parent().siblings()).slideUp();
$(this).next("ul").slideDown();
return false;
});
});
</script>

</head>
<body>

<!-- body内部はご質問文のHTMLのまま -->

</body>
</html>
    • good
    • 0
この回答へのお礼

度々ありがとうございます。
私の方で何かミスがあったらしく、ご提示いただいたものを適用したところ、最も期待する動作でした。
こんな数行でできるとは驚きです。

ぼんやりとした理解の上でですが、クリックされたメニューの背景色を変更する方法も、slideDown()後に以下を追加することで適用することができました。
$("a.category", $(this).parent().siblings()).css("background-image", "");
$(this).css("background-image", "url(image/gradation_green_R.jpg)");


唯一、この方法(ul、liがposition指定でない)と、jquery.corner.jsで角丸がうまく表現されません。
jquery.corner.jsにこだわっているわけではありませんが、IE8において、他に角丸にさせる方法などありますでしょうか?

もう少しだけお付き合いいただけたら幸いです。

お礼日時:2014/11/18 13:52

こんにちは。



ざっと見ただけなので、なさりたいことを細部まで理解していませんが、とりあえず、ご質問のメニュー開閉に関してのみ…


位置調節をCSS側で行うようにしてしまえば、スクリプトでは開閉操作のみ対応すればよいことになるので、次のように一般化できそうな気がします。

・クリックされた要素の親(LI)の兄弟要素に含まれるULを全て閉じる
・クリックされた要素の次の兄弟要素(UL)を開く
開閉動作を同時に行うなら連続して記述。閉じてから開くならコールバックで処理すればよろしいかと。


ということで、一例としてこんな感じでは?

(全角空白のインデントは半角に)
■スクリプト
$(function(){
 $("#menu ul").hide();
 $("#menu a.category").bind("click", function(){
  $("ul", $(this).parent().siblings()).slideUp();
  $(this).next("ul").slideDown();
  return false;
 });
});


ついでに、CSSはこんな想定です。
(角丸や背景、hoverに関する記述は省略しています。)
■CSS
#menu * { padding:0; margin:0; list-style-type:none; }
#menu ul, #menu li a { display:block; float:left; }
#menu li { overflow:hidden; }
#menu li a { width:250px; height:40px; border:1px #C5C5C5 solid; }


>あまり様々なidやclassを乱発させたくなく
構造がはっきりしているので、スクリプトだけに関して言えば、categoryやabilityのクラスもUL要素の有無で判定可能ですので不要にすることも可能でしょう。(上例ではcategoryを利用していますが)
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
継続して質問をお願いいたします。

教えていただいたコードを実装すると、以下手順のパターンの場合、3階層目がslideUp()されません。
1.カテゴリ2を展開し、機能まで表示
カテゴリ1
カテゴリ2
 ├サブカテゴリ2-1
 └サブカテゴリ2-2
   ├機能2-2-1
   └機能2-2-2
カテゴリ3

2.カテゴリ1を展開
カテゴリ1
 └カテゴリ1-1
カテゴリ2
カテゴリ3

クリックされたものと関係のないすべての階層をslideUp()する方法はありますでしょうか?


また、現状はクリックされた際に無理矢理<li>を挿入することで、上位カテゴリのリスト間に間隔を空けています。
これは、下位階層が展開されるにつれて、上位階層のリストと、下位階層のリストの水平位置を一致させないためです。
(つまり、あるカテゴリAの階層を展開していくと、あるカテゴリBの表示位置はどんどん下に下がっていく)
それを実現させ、さらにslideUp()、slideDown()させる方法などはあるでしょうか?

お礼日時:2014/11/17 16:51

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!