Bootstrapにjkスムーズスクロールをちょい足し
Bootstrapは小奇麗なUIを手っ取り早く実装するのに便利なCSSとJavascriptのフレームワークです。
AffixとScrollSpyの機能を使うと、Bootstrap公式サイトのサイドメニューのようなスクロールに追随するページ内ナビゲーションが実現できます。
この機能を見ていて、jkキーでスムーズスクロールさせたいなと思ったので、やってみました。デモは以下のとおり。
順を追って作り方を説明します。
Bootstrap公式サイト サイドメニューのデザイン再現
Bootstrap公式サイトのサイドメニューは、見た感じStacked tabsなんですが、ソースを見ると実態はNav listsとなっており、docs.cssでbs-docs-sidenavに対しStacked tabs風のデザインがベタ書きされています。
CSSにあまりごちゃごちゃ書きたくないので、ここではStacked tabsの方をベースに、アイコンやアクティブ時の背景色の設定だけ抜き出して再現することにしました。
サイドメニューのHTMLは以下のとおり。
<ul class="nav nav-tabs nav-stacked bs-docs-sidenav">
<li><a href="#content_01"><i class="icon-chevron-right"></i> Content 01</a></li>
<li><a href="#content_02"><i class="icon-chevron-right"></i> Content 02</a></li>
<li><a href="#content_03"><i class="icon-chevron-right"></i> Content 03</a></li>
</ul>
docs.cssを参考に追加したCSSはこちら。
.bs-docs-sidenav .icon-chevron-right {
float: right;
margin-top: 2px;
margin-right: -6px;
opacity: .25;
}
.bs-docs-sidenav a:hover .icon-chevron-right {
opacity: .5;
}
.bs-docs-sidenav .active .icon-chevron-right,
.bs-docs-sidenav .active a:hover .icon-chevron-right {
background-image: url(../img/glyphicons-halflings-white.png);
opacity: 1;
}
.bs-docs-sidenav > .active > a,
.bs-docs-sidenav > .active > a:hover {
color: white;
background-color: #08C;
}
AffixとScrollSpyでナビゲーションのスクロール追随
Affixの使い方
Affixは特定位置までスクロールした時だけ要素をposition:fixedで固定表示できる機能です。固定化したい要素に「data-spy="affix"」と「data-offset-top="999"」の属性を追加して使用します。
data-offset-topには、固定化を始めるオフセット位置を手動で指定する必要があります。変更後のHTMLは以下のとおり。
<ul class="nav nav-tabs nav-stacked bs-docs-sidenav" data-spy="affix" data-offset-top="122">
<li><a href="#content_01"><i class="icon-chevron-right"></i> Content 01</a></li>
<li><a href="#content_02"><i class="icon-chevron-right"></i> Content 02</a></li>
<li><a href="#content_03"><i class="icon-chevron-right"></i> Content 03</a></li>
</ul>
注意点として、固定化時に対象要素のグリッド幅指定が解除されてしまうバグ(?)があるので、CSSで要素の幅を指定しておく必要があります。また、固定化時の上部余白も指定します。
.bs-docs-sidenav {
width:220px;
}
.bs-docs-sidenav.affix {
top: 20px;
}
ScrollSpyの使い方
ScrollSpyはスクロール位置に応じてナビゲーションのアクティブ状態を切り替えることができる機能です。body要素に「data-spy="scroll"」と「data-target=".xxxxx"」の属性を追加して使用します。
data-targetには、アクティブ状態を切り替える.nav要素を含むセレクタを指定します。HTMLは以下のとおり。
<body data-spy="scroll" data-target=".bs-docs-sidebar">
~中略~
<div class="span3 bs-docs-sidebar">
<ul class="nav nav-tabs nav-stacked bs-docs-sidenav" data-spy="affix" data-offset-top="122">
<li><a href="#content_01"><i class="icon-chevron-right"></i> Content 01</a></li>
<li><a href="#content_02"><i class="icon-chevron-right"></i> Content 02</a></li>
<li><a href="#content_03"><i class="icon-chevron-right"></i> Content 03</a></li>
</ul>
</div>
以上で、Bootstrap公式サイトのサイドメニューと同等のものが実現できました。
Bootstrapにjkスムーズスクロールをちょい足し
さて、ここからが本題の追加内容になります。
jquery-smooth-scrollの適用
jQueryのスムーズスクロール実装はいくつも存在するようですが、以下のプラグインがよさげな感じだったので使わせてもらうことにしました。
プラグインを読み込んだ後、以下のコードを追加するだけでナビゲーションがスムーズスクロール対応になります。
$(function() {
$('ul.bs-docs-sidenav a').smoothScroll();
});
j/kキーでスムーズスクロール
最後に、jkキーで前後の項目にスクロールできるようにします。スクロールには前述のjquery-smooth-scrollを使用しています。
$.jk = {
DOWN : 'j',
UP : 'k',
}
$(document).keypress(function(event) {
if ($(event.target).is(':input'))
return;
switch(event.which) {
case $.jk.DOWN.charCodeAt():
if ($('ul.bs-docs-sidenav li.active').length) {
if ($('ul.bs-docs-sidenav li.active').next().find('a').length) {
$.smoothScroll({
scrollTarget: $('ul.bs-docs-sidenav li.active').next().find('a').attr('href')
});
}
} else {
$.smoothScroll({
scrollTarget: $('ul.bs-docs-sidenav a:first').attr('href')
});
}
break;
case $.jk.UP.charCodeAt():
if ($('ul.bs-docs-sidenav li.active').prev().find('a').length) {
$.smoothScroll({
scrollTarget: $('ul.bs-docs-sidenav li.active').prev().find('a').attr('href')
});
}
break;
}
});
以上で完成です。
参考ページ
- nakajima/jquery-jk - GitHub
- Ryan Fitzer ≫ Using the J/K keys for Content Navigation
- livibetter/jquery-jknav - GitHub
- lightbox/jquery-keynav - GitHub
- cakebaker/jquery-jknavigable - GitHub
おわりに
ページ全体が1画面に収まってしまうような場合は、このままだと正しく動作しないのでご注意下さい。
各コンテンツの高さが低い場合などは、アクティブな見出しを強調表示したり、ページ後端に余白を追加したりといったことができれば、もっと見やすくなるかもしれません。