2008/02/08

アクティブなメニューに自動でclassを付加する

グローバルメニューやサイドバーなんかで、
現在のページを示すために色を変える、なんて事をしたりしますが、
毎回これをどうやって楽にすべぇ、とか思ってました。
1ページ1ページに手作業でつけてくのも面倒だし、
ページが多くなればミスも増えそうです。

何よりこんな単純作業に時間を費やすぐらいなら、
お気に入りの音楽聴きながらコーヒー飲んでタバコ吹かしてるほうが100倍有意義ってもんです。

なので、アクティブなメニューにclassを付加するJavascriptを作ってみました。

考え方としては、ページ内のリンク先と、自ページとのアドレスを比較、
一致するならばクラスを付加って感じです。
大抵こういったメニューはリストなどで記述してると思うので、
今回はaタグに直接クラスをつけるのではなく、
親要素に対してクラスを付加してます。

まずはグローバルメニュー用

// グローバルメニュー用
function addActiveGlobal(){
if (!document.getElementsByTagName){
return;
}
var gMenu = document.getElementById(“global”);
if(!gMenu){
return;
}
var uri = location.href.split(‘#’)[0];
// index.html等ファイル名を無視する
var file= uri.substring(uri.lastIndexOf(‘/’,uri.length)+1,uri.length);
if(file.length>0){
uri = uri.split(file)[0];
}
var gLinks = gMenu.getElementsByTagName(“a”);
addActive(uri,gLinks.length, gLinks);
}

globalというID以下の要素に対し、処理を行います。
ページ内リンクの#は要らない子なので捨てます。
親コンテンツなので、下層のファイルに対してもアクティブである必要があります。
つまり、
example.com/hoge/
example.com/hoge/index.html
example.com/hoge/index2.html
example.com/hoge/example.html
これら全てでアクティブである必要があります。
なので、最後のスラッシュ以降の文字をsubstringで切り飛ばし、ディレクトリで判別する必要があります。
この処理だけちょっとめんどい。
あ、2階層以上潜るのは今回考えてません(‘A`)

// サブメニュー用
function addActiveURI(){
if (!document.getElementsByTagName){
return;
}
var sMenu = document.getElementById(“sidemenu”);
if(!sMenu){
return;
}
var uri = location.href.split(‘#’)[0];
var sLinks = sMenu.getElementsByTagName(“a”);
addActive( uri,sLinks. length, sLinks );
}

sidemenuというID以下の要素に対し、処理を行います。
サブメニューは自分だけでいいので、#以下を捨てる以外のアドレスの加工は要りません。
ただ、index.htmlなど、ファイル名が省略可能な場合、ちょっと注意が必要で、
他のページから、hoge/でリンクされてるのに、
自ページではhoge/index.htmlなどと書かれていると、
アドレスが一致しないことになってしまいます。
省略できるものは書くのか書かないのか、そこだけ取り決めをしておかないと切ないことになります。
まぁ、index.htmlやindex.phpなど、これらが出てきたら無視するって風に作ればいいんですが。

んで最後。

// クラス名付加共通パーツ
function addActive(uri,len,links){
for( var i=0; i<len ; i++){
if(links[i].href == uri){
var parentObj= links[i].parentNode;
if(parentObj.getAttribute(“class”)){
var oldClass = parentObj.getAttribute(“class”)
parentObj.setAttribute( “class”, “active ” + oldClass);
}
else if(parentObj.getAttribute(“className”)){
var oldClass = parentObj.getAttribute(“className”)
parentObj.setAttribute(“className”, “active ” + oldClass);
}
}
}
}

IEだけはclass名を取得するときclassNameじゃないとダメなので、面倒ですが2個記述。
setAttributeでクラス名をそのままセットしてしまうと、今まで付いてたクラス名が上書きされてしまうので、
一旦保存しておいて、今回付加するactiveというクラス名と文字列連結してセットしてあげます。
activeのあとに半角スペースを入れておかないと文字がつながってしまうので、
そこだけ注意。

そんな訳で出来たもの。
グローバルメニューは赤、サイドメニューは緑になります。
サンプル
javascript
Firefoxの方は、最初にそのままソースを表示、
次に、ctrl+Aを押して全選択したあと選択部分のソースを表示ってな風にしてもらうと、
違いが分かるかもしれません(゜ヮ゜)
  _、_ 
( ,_ノ` ) じゃあ、コーヒーを頂こうか。
     ζ
    [ ̄]’E
      ̄

カテゴリー: JavaScript, Web Design, XHTML — 0:42:58

トラックバック URL :

11件のコメント »

  1. こんにちは。class属性の自動化について探していたところたどり着きました^^
    見事に動作したのですがhtmlで
    <li class="hoge">リンクタグ</li>
    のようにliにクラスを設定しなければならないようですが
    これは仕様上の問題などでしょうか?
    cssでli要素にデザインを直接設定しておりこれが必要ないのであれば
    もっとすっきりできると思うのですが・・・

    コメント by やす — 2008/12/22 (月) @ 18:55:13

  2. ご質問ありがとうございます。

    リストの背景をcssで一つ一つ画像に置き換えてた場合なんかに、
    class振ってたほうがいいかなーと思ったので、つけてるだけです。

    liにクラスを利用しない場合は
    if(parentObj.getAttribute("class"))
    else if(parentObj.getAttribute("className"))
    の両方ともリターンがfalseになってしまうので

    if(parentObj.getAttribute("class")){
    var oldClass = parentObj.getAttribute("class")
    parentObj.setAttribute( "class", "active " + oldClass);
    }
    else if(parentObj.getAttribute("className")){
    var oldClass = parentObj.getAttribute("className")
    parentObj.setAttribute("className", "active " + oldClass);

    を下のように書き換えてやる必要があります。

    parentObj.setAttribute( "class", "active");
    parentObj.setAttribute("className", "active ");

    コメント by mick — 2008/12/24 (水) @ 13:56:53

  3. お返事ありがとうございます!思うように動作させることができました^^
    jsは全くわからないもので非常に参考になります。
    これを機に是非また遊びにきたいと思います。これからも頑張ってください☆

    コメント by やす — 2008/12/24 (水) @ 14:57:56

  4. こんにちは。はじめましてm(_ _)m
    テキストのメニューをハイライトする方法を探していたところたどり着きました。
    今回のサンプルは、2階層以上潜るのは考えていないとのことですが、
    どうしたら2階層以上潜ってもハイライトになるでしょうか?
    できたら、サブカテゴリー(サブフォルダ)を作って、ファイルを制御したいのですが・・・
    よろしくお願いいたします。

    コメント by いの — 2009/8/18 (火) @ 16:40:13

  5. いのさんこんにちは。
    グローバルメニューでの処理の
    >var file= uri.substring(uri.lastIndexOf(’/',uri.length)+1,uri.length);
    で、単純に最後の/しか拾ってないためです。
    2階層、3階層潜るのであれば、この処理を繰り返し、親メニューのリンク先と、
    親階層が一致するか否かを判別してやらにゃなりません。
    URLから/がいくつあるのかを数え、適切な回数分繰り返して、親メニューと一致するかどうか判別してやればうまく行くと思いますよ。

    もしくは、クラス名付加共通パーツの部分、
    >if(links[i].href == uri)
    ==で完全一致かどうかか見てますが、str.searchあたりで部分一致で見てやればもっと楽に出来るかもしれません。

    コメント by mick — 2009/8/19 (水) @ 12:59:54

  6. 迅速なご対応どうもありがとうございます。
    ですが、すみません。。。js、全然分からないんです(T-T)
    お返事を頂いてから、いろいろやっていますが、うまくいきません。。。
    お手数をおかけして申し訳ありません。
    できたら、「○○をXXに書き換える」と教えていただけないでしょうか?

    コメント by いの — 2009/8/19 (水) @ 15:48:32

  7. 分からないのであれば、この機会に勉強していただければと思います。
    残念ながらこのサンプルは、「○○をXXに書き換える」程度で複数階層に対応できる作りにはなっていませんし、コピペすりゃどんな環境でも動くぜ、ヘヘイ!ということを想定しているわけでもありません。

    考え方のヒントにでもなればな、と思って公開しているわけですので。

    もしご自分の環境に合わせて作ってくれ、というのであれば、
    コンタクトページからご予算と希望納期を明記の上、ご連絡ください(゚ヮ゚)

    コメント by mick — 2009/8/20 (木) @ 17:02:56

  8. サブメニューをアコーディオンメニューで作成しておりまして、メニューのアクティブ化を実現しようと思っていましたので、非常に助かりました。
    1点ご質問ですが、今回下記のような感じで

    document.write('メニュー1タイトル');
    document.write('');
    document.write('メニュー1_1');
    document.write('メニュー1_2');
    document.write('');
    document.write('');
    document.write('メニュー2タイトル');
    document.write('');
    document.write('メニュー2_1');
    document.write('メニュー2_2');
    document.write('');
    document.write('');

    サイドメニューのタイトル個別にid(fxmn1、fxmn2・・・)を振り分けています。
    よって、現状はidの数ごとmenu.jsを制作しています。
    (menu1.js、menu2.js・・・)

    なんとか1つのmenu.jsファイルで管理したいと思っているのですが、可能でしょうか?
    可能でございましたらご教示いただけましたら非常に助かります。

    コメント by lsj — 2011/6/21 (火) @ 15:17:13

  9. すいません、javascriptでそのまま記載するとうまく表示されないようでしたので、再度表示します。

    サブメニューをアコーディオンメニューで作成しておりまして、メニューのアクティブ化を実現しようと思っていましたので、非常に助かりました。
    1点ご質問ですが、今回下記のような感じで

    メニュータイトル1

    メニュー1_1
    メニュー1_2

    メニュータイトル2

    メニュー2_1
    メニュー2_2

    サイドメニューのタイトル個分、id(fxmn1、fxmn2・・・)を振り分けています。
    よって、現状はidの数ごとmenu.jsを制作しています。
    (menu1.js、menu2.js・・・)

    なんとか1つのmenu.jsファイルで管理したいと思っているのですが可能でしょうか?
    お忙しい処誠に恐れ入りますが、ご教示いただけましたら非常に助かります。

    コメント by lsj — 2011/6/21 (火) @ 15:21:44

  10. 何度もすいません。
    html記載箇所が表示されないようですので、おわかりいただける範囲で結構ですので、もしご教示いただけましたらよろしくお願い申し上げます。

    コメント by lsj — 2011/6/21 (火) @ 15:23:46

  11. すいません。
    自己解決できました。

    お騒がせしまして申し訳ございません。

    単純に、関数の個数を必要なだけ名前を変えて記載してあげれば解決しました。

    コメント by lsj — 2011/6/21 (火) @ 15:37:27

ページTOPへ