就職してもうすぐ3年になりますが、初めて病欠しました。17日の木曜日から熱と下痢でダウンしており、丸々二日パソコンさえ触らないという状況でした。
それはともかく、Bootstrapの公式サイトにもあるサイドバーのメニュー。

スクロールして一番上まで来たら追従するようになり、さらにヘッダーまでくるとその位置でストップするような動きになっています。そして、そのメニューはメインの内容に対応して現在の位置がどこか示してくれます。前者はAffix、後者はScrollSpyという方法を使うと実装できるようです。
というわけで作ってみたのそのメモ。
HTML
<div class="container">
<header>HEADER</header>
<div class="row">
<div class="col-sm-3">
<nav class="affix-nav">
<ul class="nav">
<li><a href="#article1">1番目</a></li>
<li><a href="#article2">2番目</a></li>
<li><a href="#article3">3番目</a></li>
<li><a href="#article4">4番目</a></li>
<li><a href="#article5">5番目</a></li>
</ul>
</nav>
</div>
<div class="col-sm-9" id="main">
<article id="article1">
<h1>1番目</h1>
</article>
<article id="article2">
<h1>2番目</h1>
</article>
<article id="article3">
<h1>3番目</h1>
</article>
<article id="article4">
<h1>4番目</h1>
</article>
<article id="article5">
<h1>5番目</h1>
</article>
</div>
</div>
<footer>FOOTOR</footer>
</div>
CSS
header,footer{
height:300px;
text-align:center;
font-size:50px;
line-height:300px;
background-color:#eee;
}
#main article{
height:300px;
border:1px solid black;
margin:20px 0;
}
.affix-nav{
width:263px;
}
.affix-nav.affix{
top:0;
position:fixed;
}
.affix-nav.affix-top{
position:static;
}
.affix-nav.affix-bottom{
position:absolute;
}
.affix-nav .nav li{
background-color:#8f8;
border-left:1px solid black;
border-right:1px solid black;
border-bottom:1px solid black;
}
.affix-nav .nav li:first-child{
border-top:1px solid black;
}
.affix-nav .nav li.active{
background-color:#4c4;
}
JavaScript
$(function(){
$('nav.affix-nav').affix({
offset:{
top: 300,
bottom:300
}
}).on('affix.bs.affix', function () {
$(this).css({
'top': '0'
});
console.log('affix.bs.affix');
}).on('affixed.bs.affix', function(){
console.log('affixed.bs.affix');
}).on('affix-bottom.bs.affix', function(){
console.log('affix-bottom.bs.affix');
}).on('affixed-bottom.bs.affix', function(){
console.log('affixed-bottom.bs.affix');
});
$('body').scrollspy({ target: 'nav.affix-nav' })
});
3~19行目がAffixの記述(といっても、console.logをやっているだけのところは必要ないですが)、21行目がScrollSpyの記述です。
実行サンプル:Bootstrap3のAffixとScrollspyを試してみた
ブラウザの高さを狭くして、一番下までスクロールしたらフッターの上でメニューの位置がストップするのがわかると思います(とはいっても、Chromeでしか動作確認してないのですが・・・)。
以下、作成する際に手こずったこと。
はじめ、スクロールを一番下までやった後にスクロールを上に戻すと、なぜかフッターの直前でメニューがストップしたままになってしまっていました(一番上までやるともとに戻る)。とりあえず、Affixにはいろいろイベントが用意されているようなので、affix.bs.affix(affixが発生する直前に発生)、affixed.bs.affix(affixが発生した直後に発生)、affix-bottom.bs.affix(affixが最下部に到達した時に発生)、affixed-bottom.bs.affix(affixが最下部に到達した直後に発生)というイベントを呼ぶとそれぞれのイベント名をコンソールに吐き出すようにしてみることに。すると、一番下までスクロールした時に『affix-bottom.bs.affix』『affixed-bottom.bs.affix』という順番に呼ばれた後、もう一度上にスクロールすると『affix.bs.affix』『affixed.bs.affix』が呼ばれるのだけれども、またそのすぐ後に『affix-bottom.bs.affix』『affixed-bottom.bs.affix』が呼ばれているのを確認。この中間の『affix.bs.affix』『affixed.bs.affix』が呼ばれた時点でのメニューの属性を確認してみるとstyleのtopがaffix-bottomの時のままになっていました。いろいろ調べて原因が分からなかったのでとりあえず『affix.bs.affix』が呼ばれた時点でtopを0にするようにしています(上記JavaScriptのコードの10行目)。でも、こんなことやってるところなさそうなので、やっぱり自分の何かが間違っているのだと思います。
ちなみに、『affix.bs.affix』とかのイベントは古いBootstrap3のバージョンでは動いてくれませんでした。今回のサンプルは現在最新のバージョン3.1.1で試しています。後、CSSの指定も意外と重要っぽいです。クラスがaffix-topの時の指定、クラスがaffixの時の指定、クラスがaffix-bottomの時の指定はちゃんと書いたほうがよさそうでした(上記CSSのコードの16行目から25行目)。
ScrollSpyのほうは、たいして手こずることはなかったのですが、1つ手こずったのがメニューのクラスに”nav”をつけなければいけないということに最初気づかず、迷いました。なんでnavクラスだけにしてるんだろう・・・。
余談ですが、Affixの機能は一般的にスティッキーサイドバーと呼ばれています。予想できるかもしれませんが、メニューではなくヘッダー追従型だと、スティッキーヘッダーという名前になります。ScrollSpyもBootstrap独自の名前っぽいので、一般的な呼び名というのがあるのかもしれません。

コメント