実際に記述するスクリプト。(CSSのみでポップアップメニューが実現出来ない環境への対策)
- この文書は旧版のアーカイヴです。実際にご利用になりたい方は、より汎用性を高めたヴァージョンがございますので、そちらもご覧ください。
- 本記事は、CSS小技集内のCSSだけで実現するポップアップメニューの補足記事として書いております。
- 本記事でのスクリプトを実際に利用したサンプルは、以下で公開しております。
実際に記述するスクリプト。
原則としてCSSでポップアップメニューを実現します(再掲載)。
先ず基本として、CSS第二水準に準拠している環境であればCSSだけでポップアップメニューを実現する事が可能であるため、これらにはCSSで対応するものとします。
- 対応出来る環境は今日では以下のようになります。
- インターネットエクスプローラ 7.0以降(7.0は非標準なやり方になりますが実現は可能です)
- ファイヤーフォックス 3.0以降
- オペラ 7.5以降
- サファリ 3.0以降(ウィンドウズ版は 3.0.3以降), グーグルクロームなど
- プレイステーションなどに採用されているネットフロントについては対応しないかも知れません。
実際にCSSで対応するための方法は、以下の記事をご覧ください。
尚、以下、上記の記事をご覧になった事を前提に話を進めていきます。
CSSで実現するためのHTMLのマークアップとCSSでのスタイル定義について、最低でも以下の点は抑えておいてください。
- ポップアップメニューの本体は、
MENU_NAVIと言うIDを与えられた<ul>要素である。 - ポップアップメニューの各列は、
MENU_NAVIと言うIDを与えられた<ul>要素直下の<li>要素である。- 見出し部分はその一番目の子要素である、<p>要素となる。
- 子メニューは二番目の個要素となる<ul>要素である。
- これらの要素はHTML文書中の任意の位置に記述可能である(CSSのポジショニングを用いれば、位置調整は自由自在であるため)。
- メニュー本体はポジショニングで文書冒頭部の中央に表示される。
- 通常、子メニューは非表示となっているが、見出し部分がマウスでポイントされると、子メニュー部分がCSSの
:hover擬似クラスを与えられた<p>要素に続く<ul>要素を指定する隣接セレクタで指定されたセレクタに依り、表示されるようになる。
ファイヤーフォックス 2.0向けのスクリプト。
ファイヤーフォックス 2.0に対しては、ファイヤーフォックス 2.0の問題点で述べた通り、display: inline-block; プロパティの未サポートが原因です。
それさえ対応すれば、問題はなくなるのです。
一方、実際にJAVAスクリプトのコードで書くべき事は、
この二つとなります。
実際に、ファイヤーフォックス 2.0向けのコードを書いてみましょう。
column_width=10;// 各項目の幅(文字数単位)pm_onload=window.onload;var i;var ua=navigator.userAgent;/* ファイヤーフォックス 2.0とそれに相当するゲッコウブラウザであれば、スタイル調整をする */if (document.getElementById && ua.indexOf('Gecko/')>-1 &&(i=ua.indexOf('rv:'))>0 && parseFloat(ua.substring(i+3,ua.length))<1.9) {window.onload=style_firefox2;}/***************************************************************/ /* ファイヤーフォックス 2.0でのスタイル調整 */function style_firefox2() {if (pm_onload) pm_onload();var ele=document.getElementById('MENU_NAVI');if (!ele) return;// 先ず、フォントの大きさを診る。var s=document.defaultView.getComputedStyle(ele, '');var font_size=parseInt(s.fontSize);// 続いて、画面の幅を調べる。var screen_width=window.innerWidth;// 更に、メニューバー直下の<li>要素の数を調べる。var eles=ele.childNodes;var i=-1;var child_number=0;while (++i<eles.length) {if (eles[i].nodeType==1) child_number++;}// 左端の位置を確定させる。var dx=font_size*column_width;var x=(screen_width-child_number*dx) >> 1;// 各項目をあるべき位置にポジショニングする。i=-1; var j=0;while (++i<eles.length) {if (eles[i].nodeType!=1) continue;eles[i].style.position='absolute';eles[i].style.left=x+'px';eles[i].style.top='0';x+=dx;}}
ファイヤーフォックス 2.0向けのスクリプトの説明。
初めに述べた通り、
の二つがあります。
ファイヤーフォックス 2.0であると判断された場合のみ必要な処理を行う処理の説明。
簡単に記述すると、以下のようになります。
- 先ず、onload(HTML文書等を全て読み終わった時点) で実行されるべき処理があれば、その処理を変数
pm_onloadに退避させ、 - その上でユーザエージェント文字列を
navigator.userAgentプロパティから取得し、 - それがファイヤーフォックス 2.0の条件に合致したら、ファイヤーフォックスでの本処理である函数
style_firefox2()を onload で実行するようにします。
ユーザエージェント文字列が、ファイヤーフォックス 2.0のものである条件は、
Gecko/が含まれ、rv:と言う文字の直後の小数が 1.9 未満である
場合となります。
- 当然、ファイヤーフォックス 2.0に相当するゲッコウを実装したもじら系ブラウザ(フロック, カミーノなど)もチェック対象となります。
- 尚、この方法はファイヤーフォックス 1.0未満も対象となりますが、やはりCSSの実装に不備があるので処理対象とするのは問題ないでしょう(ポップアップはしないようですが)。
ファイヤーフォックス 2.0での本処理の説明。
簡単に記述すると、以下のようになります。
- 先ず、事前に onload(HTML文書等を全て読み終わった時点) で実行されるべき処理を実行しておきます。そのうえで、ポップアップメニューとなる要素(ここでは前提条件で述べた通り、
MENU_NAVIと言うIDが与えられた<ul>要素)が存在しなければその時点で処理を中断します。 - 続いて、ポップアップメニューとなる要素の各列の幅をピクセル単位で算出します。
- 先ず、
document.defaultView.getComputedStyle(ele, '')メソッドで、ele変数に収められているMENU_NAVIと言うIDを与えられた要素の具体的なスタイル値を取得します。document.defaultView.getComputedStyle()メソッドとは、引数で指定された要素オブジェクトに与えられるスタイルの具体的な値を集めたオブジェクトを返します。例えば、font-sizeプロパティであれば、具体的な値と言う事でピクセル単位の値が得られます。
- 続いて、その中から、font-sizeプロパティの具体的な値を取得します。
parseInt()メソッドに依り、整数化した値が得られます。
- そして、予めスクリプトの冒頭に記述している、各列の文字数を乗じて、実際の各列のピクセル数を算出すると言う訳です。
- 先ず、
- この上で、画面の幅と項目数を調べ、一列目がどの位置に来るかを算出します。
- 前提条件で述べた通り、センタリングして表示するため。
- 最後に、各列をあるべき位置に左から順にポジショニングで並べて行きます。
インターネットエクスプローラ 6.0向けのスクリプト。
インターネットエクスプローラ 6.0での問題点は余りにも多く、CSSだけでのポップアップメニューの実現は不可能です。
そこで、インターネットエクスプローラ 6.0に対しては、従来通りのJAVAスクリプトとDOMを活用する方法で対処するしかありません。
この場合、処理は大きく分けて以下のようになります。
- インターネットエクスプローラ 6.0である事を判定する処理
- インターネットエクスプローラ 6.0での前処理
- (インターネットエクスプローラ 6.0でのみ)見出しにマウスカーソルが乗った時の処理
- (インターネットエクスプローラ 6.0でのみ)子メニューにマウスカーソルが乗った時の処理
- (インターネットエクスプローラ 6.0でのみ)見出しや子メニューからマウスカーソルが去った時の処理
実際に、ファイヤーフォックス 2.0向けのコードを書いてみましょう。
pm_onload=window.onload;var i;var ua=navigator.userAgent;// ファイヤーフォックス 2.0とそれに相当するゲッコウブラウザであれば、スタイル調整をする。if (document.getElementById && ua.indexOf('Gecko/')>-1 &&(i=ua.indexOf('rv:'))>0 && parseFloat(ua.substring(i+3,ua.length))<1.9) {window.onload=style_firefox2;}// IE 6.0であれば、スタイル調整をする。if (document.all && ua.indexOf('Opera/')<0 && ua.indexOf('Opera ')<0 &&(i=ua.indexOf('MSIE '))>0 && parseFloat(ua.substring(i+5,ua.length))==6.0) {window.onload=style_IE6;}/***************************************************************/ /* IE 6.0でのDOM操作 */function style_IE6() {if (pm_onload) pm_onload();var ele=document.all['MENU_NAVI'];if (!ele) return;// メニューバーの幅を100%にする。i=document.documentElement.clientWidth || document.body.clientWidth;ele.style.pixelWidth=i;// 各列をあるべき位置に配置し、マウス操作でスクリプトを起動出来るようにする。var eles=ele.childNodes;var eles2,eles3;i=-1;while (++i<eles.length) {if (eles[i].nodeType!=1) continue;eles[i].onmouseover=show_IE6;//見出しにカーソルが乗ったら子メニューを出す。eles2=eles[i].getElementsByTagName('ul');eles3=eles[i].getElementsByTagName('p');if (!eles2[0] || !eles3[0]) continue;eles2[0].innerHTML='<li><p>'+eles3[0].innerHTML+'</p></li>'+eles2[0].innerHTML;eles2[0].style.marginTop='0';eles2[0].onmouseover=show2_IE6;//子メニューにカーソルが乗ったら消えないようにする。eles2[0].onmouseout=hide_IE6;//子メニューからカーソルが去ったら消す。}}// 全ての子メニューを消す。function hideAll_IE6() {var ele=document.all['MENU_NAVI'];var eles=ele.childNodes;var eles2;i=-1;while (++i<eles.length) {eles2=eles[i].getElementsByTagName('ul');if (!eles2[0]) continue;eles2[0].style.display='none';}}// ポイントされたメニューの子メニューを表示させる。function show_IE6() {// 一旦、全ての子メニューを消す。hideAll_IE6();// その上で、対象となる子メニューを表示させる。var eles=this.getElementsByTagName('ul');if (!eles[0]) return;eles[0].style.display='block';}// 子メニューにマウスがある場合には消さないようにする。function show2_IE6() {this.style.display='block';}// 子メニューからマウスが外れた場合には消してしまう。function hide_IE6() {this.style.display='none';}
インターネットエクスプローラ 6.0向けのスクリプトの説明。
初めに述べた通り、
- インターネットエクスプローラ 6.0の判定
- インターネットエクスプローラ 6.0用前処理
- 見出しにマウスカーソルが乗った時の処理
- 子メニューにマウスカーソルが乗った時の処理
- 見出しや子メニューからマウスカーソルが去った時の処理
の五つがあります。
インターネットエクスプローラ 6.0用前処理の説明。
- 先ず、インターネットエクスプローラ 6.0ではCSSの実装上の不備より、ポップアップメニューを入れたバーがきちんと横幅一杯にならないため、横幅一杯になるようにスタイルを調整します。
- 続いて、各列をあるべき位置にポジショニングして、スクリプトがマウスカーソルの移動で起動するようにします。
- 各列の見出しに対応する子メニューの上部に、見出しの部分が重なるように付け足します。こうする事で、マウスカーソルが見出しに乗ったときに、その見出しに重ねて見出しの付いた子メニューが表示されるようになります。
- 子メニューか見出しのどちらかにカーソルがある間は子メニューは表示したままにしておくのですが、見出しと子メニューが並んだ状態だとこれが巧くいきません(カーソルが消えてから一定時間経つまでは消さないと言う方法もありますが)。このため、見出しにカーソルが乗ったら、子メニュー上にカーソルが載るように子メニューに見出し部分を付け足す事でこの問題を解消しているのです。
- 見出し部分には、マウスカーソルが乗ったときに見出しが付いた子メニューを重ねて表示させる処理を実行させるようにします。
- 一方、子メニュー部分には、
- マウスカーソルが乗っている間は子メニューを消さないようにする処理を実行するようにし
- マウスカーソルが離れたときに子メニューを消す処理を実行するように
します。
- 各列の見出しに対応する子メニューの上部に、見出しの部分が重なるように付け足します。こうする事で、マウスカーソルが見出しに乗ったときに、その見出しに重ねて見出しの付いた子メニューが表示されるようになります。
最終的に書かれるべきスクリプト。
ファイヤーフォックス 2.0向けの処理と、インターネットエクスプローラ 6.0向けの処理を合わせたスクリプトは、実際のスタイルシートとスクリプトをご覧ください。
- このスクリプトは、CSSだけで実現するポップアップメニューで解説しているスタイルシートとの併用を前提としております。
- このスクリプトについて制作者は著作権を主張しません。どのように改変してお使いいただいても構いませんが、ご利用された結果につきましては免責とさせていただきたいと存じます。
最後に。
インターネットエクスプローラには behaviorプロパティ、ファイヤーフォックスには -moz-bindingプロパティがあり、それぞれCSSスタイルシートからXML文書を参照出来るようになっております。
このXMLファイルにはいずれもJAVAスクリプトを記述する事が可能で、これに依りJAVAスクリプトをスタイルシートとして記述する事が可能になります。
- 但し、いずれもあくまでも非公式なものですので、現在のところオペラやサファリにはいずれも対応しておりません。
今回の場合、問題になっているブラウザはいずれもこの方法が利用出来るため、CSSで今回紹介したスクリプトをこの方法で利用出来るようにすれば、ISO-HTMLやXHTML 1.0 ベーシックのようなJAVAスクリプトに対応していない文書型でも、ポップアップメニューを活用した賑やかな(喧しい?)ウェブサイトを構築する事が可能になるでしょう。