Note: この記事は、3年以上前に書かれています。Webの進化は速い!情報の正確性は自己責任で判断してください。
ボタン・クリックで滑り落ちるメニューを作ってみた。昔のShaunInman.comとか、今で言うjek2k.comにあるようなやつ。大きめのフッタとセットで提供されることが多いように思います。
今回のポイントは3つ。
- 開ききった時の「高さ」を取得
- 現時点での「高さ」を取得
- 連打時の処理中断(重複)への対応
Sample
起動部分
起動時のメニューの高さから、これから開くか閉じるかを判別する。「timer」の値を処理開始と連動させることで重複を避けています。
var timer = null;
slide = function(objID) {
var obj = document.getElementById(objID);
obj.style.overflow = 'hidden';
var o = getElementSize(obj);
var slow = 3;
var speed = 30;
if(obj.offsetHeight == 0) {
obj.style.display = 'block';
obj.style.height = 0;
if(timer == null) this.down(obj,o.oh,slow);
}
else {
if(timer == null) this.up(obj,o.oh,speed);
}
}
たとえば処理が完了する前にボタンを押した場合、前の処理が終了していないために動作が被り、小刻みに震えるだけになってしまう。clearTimeoutで処理を中断することもできますが、その方法だと連打時にあまり信頼できない結果でした。
結局、処理が完了するまで操作をロックしておく方法に落ち着きました。
要素の高さを取得
メニューの開放時および現時点での高さを取得する。特に開放時の高さは、メニューを開くときの限界点として必要になってきます(無いと永遠に落ち続けることになる)。
getElementSize = function(elm) {
var oSize = new Object();
var elm2 = elm.cloneNode(true);
if(-1 == navigator.userAgent.indexOf('MSIE 5')){
elm2.style.padding = '0';
elm2.style.border = '0';
}
elm2.style.display = 'block';
elm2.style.position = 'absolute';
elm.parentNode.appendChild(elm2);
oSize.oh = elm2.clientHeight;
oSize.ow = elm2.clientWidth;
elm.parentNode.removeChild(elm2);
return oSize;
}
height値を直接取得するのではなく、要素のクローンを作成してシミュレートする手法をとっています。よって、明示的にheightを指定していない場合でも値が取れるようになっています。
開放部分
メニューが滑り落ちる部分。静止するポイントと、スムーズに静止するための減速率を必要とする。
this.down = function(obj,limit,slow) {
var present = getElementSize(obj).oh;
var distance = limit-present;
var moveValue = Math.max(Math.floor(distance / slow),slow);
if(present < limit) {
obj.style.height = (present+moveValue) + 'px';
timer = setTimeout(function(){ this.down(obj,limit,slow) }, 10);
}
else {
obj.style.height = limit + 'px';
timer = null;
};
}
閉鎖部分
メニューがせりあがる部分。減速率と同じように、速度もカスタム可能。
this.up = function(obj,ori,speed) {
var present = getElementSize(obj).oh;
var moveValue = speed + 3;
present = present-moveValue;
if(present > 0) {
obj.style.height = present + 'px';
timer = window.setTimeout(function(){ this.up(obj,ori,speed) }, 10);
}
else {
obj.style.height = ori + 'px';
obj.style.display = 'none';
timer = null;
};
}
JQuery
実は、同じ動作をJQueryでやると3行で済んでしまう。
$(document).ready(function(){
$("a.showMenu1").click( function() {$("#menu").slideToggle("normal");} );
});
ただし、速度の調節はできない。
memo: ちなみに、このスクリプトとjQuery版とを比べてみた場合、ある環境において明らかにパフォーマンス的に劣る。いろいろ無駄な処理が多いからだろうけど、そこら辺が今後の課題。
Note: スパム対策が面倒なので、コメント投稿を廃止しました。以前のコメントは残します。
ご意見・ご要望はtwitter@sigwygかはてブコメントにて。