見出し画像

📦JavaScriptで手軽にサイドバーを追従できるSticky Sidebar

スクロールしてもサイドバーが常にピタッと固定してくっついてくるような動作を実装できるJavaScriptライブラリです。非常に軽量でrequestAnimationFrameを使っているため、パフォーマンスに優れています。対応ブラウザもIE9を含むほぼすべてのブラウザに対応しており、ブログや実務など幅広く使えます。

デモ

まずはどのように動作するかを見てみましょう。公式には2つの例が用意されています。

サイドバーよりメインのコンテンツ量の方が多い例
メインよりサイドバーのコンテンツ量の方が多い例

最後にサンプルファイルも用意しているので、ダウンロードしてみてください。

インストール

主に3つの始め方があるので、好きな方法で手軽に始められます。

CDNから

jsDelivrのWebサイトに行き、最新バージョンのsticky-sidebar.min.jsのURLをコピーします。

<script src="https://cdn.jsdelivr.net/npm/sticky-sidebar@3.3.1/dist/sticky-sidebar.min.js"></script>

あとはscript要素を使って読み込めばよいです。

GitHubから

GitHubのWebサイト上からzipファイルをダウンロードする方法です。リポジトリに移動し、[Clone or donwload]→[Download ZIP]をクリックするとダウンロードできます。

npmから

npmコマンドでインストールできます。

$ npm i sticky-sidebar

インストールしたらESモジュールとしてインポートします。

import StickySidebar from 'sticky-sidebar'

使い方

まずは基本的な使い方です。

<div class="wrapper">
  <aside class="sidebar">
    <div class="sidebar__inner">
      サイドバーのコンテンツ
    </div>
  </aside>
  <main class="main">
    メインのコンテンツ
  </main>
</div>

HTMLの構造は決まっています。.sidebarと.mainを囲む.wrapper要素が必要です。また、サイドバーは内部に.sidebar__inner要素が必要です。

.wrapper {
  display: flex;
  max-width: 700px;
  margin: 2.5em auto;
}
.sidebar {
  width: 30%;
  margin-right: 1em;
}
.main {
  flex: 1 0 0%;
  min-width: 0;
}

Flexboxを使って.sidebarと.mainを横並びにしています。

new StickySidebar('.sidebar', {
 containerSelector: '.wrapper',
 innerWrapperSelector: '.sidebar__inner',
})

あとはJSプラグインを実行するだけです。HTMLの構造に合わせてクラス名の指定は変更してください。しかし、これだけだと実際の動作が分かりにくいかもしれません。動作確認用のCSSを用意したので、追加してみてください。

.sidebar__inner {
  position: relative;
  height: 150vh;
  border: 1px solid #1c7ed6 ;
  background-color: #e7f5ff ;
}
.sidebar__inner::before,
.sidebar__inner::after {
  position: absolute;
  left: 0;
  padding: .25em .5em;
  color: #fff ;
  font-size: .8em;
  background-color: #1c7ed6 ;
}
.sidebar__inner::before {
  top: 0;
  content: '↑.sidebar上部';
}
.sidebar__inner::after {
  bottom: 0;
  content: '↓.sidebar下部';
}
.main {
  position: relative;
  height: 300vh;
  border: 1px solid #d6336c ;
  background-color: #fff0f6 ;
}
.main::before,
.main::after {
  position: absolute;
  left: 0;
  padding: .25em .5em;
  color: #fff ;
  font-size: .8em;
  background-color: #d6336c ;
}
.main::before {
  top: 0;
  content: '↑.main上部';
}
.main::after {
  bottom: 0;
  content: '↓.main下部';
}

CSSを追加すると手軽に動作を確認できます。

実践的な使い方

ここまでは基本的な使い方の紹介でしたが、実際に使う場面を考えてみるとパソコンのときはサイドバーがありますが、スマホのときはサイドバーはありません。より実践的なレスポンシブに対応するための使い方を紹介します。

/**
* Sticky Sidebar
*/

const sidebar = new StickySidebar('.sidebar', {
 containerSelector: '.wrapper',
 innerWrapperSelector: '.sidebar__inner',
})

/**
* メディアクエリ
*/

const mediaQuery = matchMedia('(min-width: 768px)')
const mediaQueryHandler = mq => {
 if (mq.matches) {
   // Sticky Sidebar有効化
   sidebar.initialize()
 } else {
   // Sticky Sidebar無効化
   sidebar.destroy()
 }
}
mediaQueryHandler(mediaQuery)
mediaQuery.addListener(mediaQueryHandler)

メディアクエリを使って画面幅が768px以上のときはサイドバーを追従させるようにしています。一応、レスポンシブ対応のためのminWidthオプションも用意されているのですが、希望通りの動作にならなかったので使っていません。

.sidebar {
   display: none;
 }
 @media (min-width: 768px) {
   .sidebar {
     display: block;
   }
 }

そして、CSSで画面幅が768px未満のときは非表示にします。これでレスポンシブに対応できます。

パフォーマンス

CSSでGPU描画するように指定するとパフォーマンスを向上できます。

.sidebar__inner {
  transform: translate3d(0, 0, 0);
  will-change: position, transform;
}

また、サイドバーが追従しているときleft: 47px;のように整数px値が指定されるため、追従しているときとしていないときで若干のズレが生じる場合があります。

.sidebar__inner {
  left: auto !important;
}

そんなときはleftプロパティにautoを指定するとよいです。

オプション

色々なオプションが用意されています。

topSpacing

サイドバー追従中の上の余白を指定します。初期値は0です。

new StickySidebar('.sidebar', {
  topSpacing: 50,
})

bottomSpacing

サイドバー追従中の下の余白を指定します。初期値は0です。

new StickySidebar('.sidebar', {
  bottomSpacing: 50,
})

topSpacingとbottomSpacingはオプションで指定するよりも以下のようにCSSで指定しておいた方が調整しやすいと思います。

.sidebar__inner {
  padding: 2em 0;
}

containerSelector

サイドバーとメインを囲む要素を指定します。初期値はサイドバーとメインの親要素ですが、セレクタで指定した方がパフォーマンスがよいです。

new StickySidebar('.sidebar', {
 containerSelector: '.wrapper',
})

innerWrapperSelector

サイドバー内部を囲む要素を指定します。初期値は、存在しない場合は.inner-wrapper-sticky要素が自動で挿入されますが、これもパフォーマンスを考えるとあらかじめ指定しておいた方がよいです。

new StickySidebar('.sidebar', {
 innerWrapperSelector: '.sidebar__inner',
})

resizeSensor

使い所がいまいち分かりませんでしたが、ResizeSensor.jsとあわせて使う場合に指定します。初期値はtrueです。

new StickySidebar('.sidebar', {
  resizeSensor: true,
})

stickyClass

サイドバー追従中に追加されるクラス名を変更できます。初期値はis-affixedです。

new StickySidebar('.sidebar', {
  stickyClass: 'is-affixed',
})

minWidth

レスポンシブ対応のために画面幅を指定しますが、先述した通り思うような動作をしなかったため使いませんでした。初期値は0です。

new StickySidebar('.sidebar', {
  minWidth: 300,
})

イベント

サイドバーの状態に応じてイベントが発火されるようになっています。

affix.top.stickySidebar

サイドバー追従中、画面の上部に固定される直前に発火します。

affixed.top.stickySidebar

サイドバー追従中、画面の上部に固定された直後に発火します。

affix.bottom.stickySidebar

サイドバー追従中、画面の下部に固定される直前に発火します。

affixed.bottom.stickySidebar

サイドバー追従中、画面の下部に固定された直後に発火します。

affix.container-bottom.stickySidebar

サイドバー追従中、.wrapperの下部に達する直前に発火します。

affixed.container-bottom.stickySidebar

サイドバー追従中、.wrapperの下部に達する直後に発火します。

affix.unbottom.stickySidebar

サイドバーが画面の下部に達しなくなる直前に発火します。

affixed.unbottom.stickySidebar

サイドバーが画面の下部に達しなくなる直後に発火します。

affix.static.stickySidebar

サイドバーが追従されなくなる直前に発火します。

affixed.static.stickySidebar

サイドバーが追従されなくなる直後に発火します。

document.querySelector('.sidebar').addEventListener('affix.top.stickySidebar', event => {
 console.log(event)
})

イベントが発火されたタイミングで自由に処理を行えます。

メソッド

updateSticky()という関数が用意されています。

const sidebar = new StickySidebar('.sidebar', {
 containerSelector: '.wrapper',
 innerWrapperSelector: '.sidebar__inner',
})

sidebar.updateSticky()

例えば、サイドバー内にアコーディオンがあり、クリックするとサイドバー自体の高さが変わるようなときに、高さを再計算させる目的で使います。

サンプル

記事中で紹介したサンプルをダウンロードできます。

画像1

この記事が気に入ったらサポートをしてみませんか?