こんな画面でサイドメニューがものすごく縦長に…
メニューが多くなってなんだかやたらと縦長になってしまったときに。
まぁ、アコーディオン化するライブラリとか使えばいいかもしれませんが、自分で実装してみました。
メニューのスクロールONとスクロールバーのデザイン
まずはmenu
部分の高さを表示高に固定し、画面からはみ出たらスクロールバーを出現させます。
ついでにスクロールバーを色々デザインしたいところですが、ブラウザによって可否はそれぞれでした。
$(function(){
//メニューエリアの高さを調整
function adjustMenuHeight() {
$('#menu').height($(window).height());
}
$(window).on('load resize', function() {
adjustMenuHeight();
});
});
# menu {
overflow: auto; /* スクロールバー必要なら表示 */
-ms-overflow-style: -ms-autohiding-scrollbar; /* IE用:スクロールバー自動非表示 */
}
/* Chrome,Safari用:スクロールバーデザイン */
# menu::-webkit-scrollbar {
width: 4px;
-webkit-border-radius: 3px;
border-radius: 3px;
}
# menu::-webkit-scrollbar-thumb {
background: #abcdef;
-webkit-border-radius: 3px;
border-radius: 3px;
}
/* FireFoxはデザインできないらしい */
Chromeはwebkitで色々好みに合わせたデザインができました。
IEは自動非表示ぐらいでしょうか。
FireFoxはデザインできないようです…。
(参考)
CSSでスクロールバーをカスタマイズ
スクロールさせる領域だけどスクロールバーは非表示にしたい。
-ms-overflow-style
コンテンツ側のスクロールがあるので…
さて、contents
側が長くなるページもあるのでその場合はメニューを追随させて常に画面に表示させるようにしたいと思います。
$(function(){
//コンテンツスクロールに追随するメニュー
var fix = $('#menu'); //固定する要素
var main = $('#contents'); //固定する要素を収める範囲
var sideTop = fix.offset().top;
var fixTop = fix.offset().top;
var mainTop = main.offset().top;
$(window).on('scroll', function() {
fixTop = fix.css('position') === 'static' ? sideTop + fix.position().top : fixTop;
var fixHeight = fix.outerHeight(true);
var mainHeight = main.outerHeight();
var winTop = $(window).scrollTop();
if (winTop + fixHeight > mainTop + mainHeight) {
fix.css({
position: 'absolute',
top: mainHeight - fixHeight
});
} else if (winTop >= fixTop) {
fix.css({
position: 'fixed',
top: 0
});
} else {
fix.css('position', 'static');
}
});
});
ここはもうほぼ参考サイトのまんまです…
サイドバーをある範囲内で固定してスクロールに追尾させるjQuery
ヘッダとフッタがあった
header
とfooter
があって、contents
のスクロール状態によって画面上に出現したり見えなくなったりするのでそれも考慮してメニューの高さを決める必要がありました。
上で書いたmenu
の高さを求める箇所をいじります。
$(function(){
//メニューエリアの高さを調整
function adjustMenuHeight() {
//header,footerが表示されているかで変動
var headerPos = $('#header').offset().top + $('#header').height();
var footerPos = $('#footer').offset().top - $(window).height();
var headerH = 0;
var footerH = 0;
//headerが見えている場合
if ($(window).scrollTop() < headerPos) {
headerH = $('#header').height() - $(window).scrollTop();
}
//footerが見えている場合
if ($(window).scrollTop() > footerPos) {
footerH = $(window).scrollTop() - footerPos;
}
var h = $(window).height() - headerH - footerH;
$('#menu').height(h);
}
});
また、contents
のスクロールに合わせてheader
・footer
の表示を判断するため、先ほどの$(window).on('scroll', function() {...}
の終わりでもこの関数を呼びます。
(参考)
【jQuery】スクロールする要素内で指定要素が表示状態かを判定する
選択したメニューの位置に移動させたい
ついでに、menu
を選択するとページ読み込みが走ってmenu
は常に先頭の位置が表示されているのも不親切なので、選択したメニューの位置まで自動スクロールするようにします。
なお、「選択したメニュー」を取る方法は色々あると思うので割愛しますが(class付与してるとか? 自分のケースではbackground-color
だけが判別可能な要素でした…)、ポイントは以下です。
$(function(){
$(window).on('load', function() {
$('#menu').animate({scrollTop:$(選択メニュー).offset().top - $('#menu').offset().top});
});
});
(参考)
たった1行!jQueryで指定箇所までアニメーションつきで自動スクロールさせる方法
jsまとめ
$(function(){
//メニューエリアの高さを調整
function adjustMenuHeight() {
//header,footerが表示されているかで変動
var headerPos = $('#header').offset().top + $('#header').height();
var footerPos = $('#footer').offset().top - $(window).height();
var headerH = 0;
var footerH = 0;
//headerが見えている場合
if ($(window).scrollTop() < headerPos) {
headerH = $('#header').height() - $(window).scrollTop();
}
//footerが見えている場合
if ($(window).scrollTop() > footerPos) {
footerH = $(window).scrollTop() - footerPos;
}
var h = $(window).height() - headerH - footerH;
$('#menu').height(h);
}
//コンテンツスクロールに追随するメニュー
var fix = $('#menu'); //固定する要素
var main = $('#contents'); //固定する要素を収める範囲
var sideTop = fix.offset().top;
var fixTop = fix.offset().top;
var mainTop = main.offset().top;
$(window).on('scroll', function() {
fixTop = fix.css('position') === 'static' ? sideTop + fix.position().top : fixTop;
var fixHeight = fix.outerHeight(true);
var mainHeight = main.outerHeight();
var winTop = $(window).scrollTop();
if (winTop + fixHeight > mainTop + mainHeight) {
fix.css({
position: 'absolute',
top: mainHeight - fixHeight
});
} else if (winTop >= fixTop) {
fix.css({
position: 'fixed',
top: 0
});
} else {
fix.css('position', 'static');
}
adjustMenuHeight();
});
$(window).on('load resize', function() {
adjustMenuHeight();
});
$(window).on('load', function() {
//選択メニューへスクロール移動
$('#menu').animate({scrollTop:$(選択メニュー).offset().top - $('#menu').offset().top});
});
});
20180510追記:コンテンツの横スクロールがある場合
contents
部分が縦だけでなく横にも長くて横スクロールがある場合(これはこれでダサいというのは別議論として)、
このままだと横スクロールしてもmenu
は画面左に固定されたままです。
ここでは横スクロールの場合はmenu
も横に(画面外に)スクロールさせてみます。
$(window).on('scroll', function() {
......
if (winTop + fixHeight > mainTop + mainHeight) {
......
} else if (winTop >= fixTop) {
//Left位置を取得
var mainScrollLeft = parseFloat($(window).scrollLeft()) * -1;
fix.css({
position: 'fixed',
top: 0,
left: mainScrollLeft
});
} else {
......
}
......
});
fixのときにleftをセットしてあげます。
marginなど計算する必要があれば計算します。(ここではしていませんが、そのためにparseしています)
(参考)
fixedの要素も横方向にはスクロールして欲しいという想い
以上です。