ページ内リンクへの移動時にスクロール位置が固定ヘッダーと被らないようにする方法

追記
IE以外の主要ブラウザでは scroll-margin-top が利用できる


固定ヘッダーがあるサイトだと、ページ内リンクをクリックしたときに対象の要素が固定ヘッダーと被ってしまうことがある。

f:id:yuheiy:20170816184501g:plain

固定ヘッダーがあるサイトの例としてVue.jsとかBootstrapのドキュメントを見ると、この問題は起こらないようになっていた。調べてみるとそれぞれ同じようなやり方で問題を回避している。

メジャーな手法なのかなと思ってググったらHash Tag Links That Don’t Headbutt The Browser Windowというドンピシャな記事があった。すでに不要になった古いハックとかにも言及していて冗長だったので、メモ代わりにこの記事に書き直しておく。


ページ内リンクへの移動時のスクロール位置は、対象の要素の座標の上端になる。なので単にpadding-topで余白を作って調整することもできる。でもそれだと、要素間のmarginの設定に影響したり、ヘッダーの高さが要素間の余白より大きい場合に対応できなかったりする。

次のような方法を用いれば問題なく解決できる。

.heading[id]::before {
  content: "";
  display: block;
  height: 8rem;
  margin-top: -8rem;
  visibility: hidden;
}

要素の上部にヘッダーの高さ分の余白を作るために高さを指定して、見た目上はその空間を作らないようにネガティブマージンで打ち消す。こういう感じに動く。

スタイルによっては余計な<span>要素を作らないといけないとかはありそうだけど、これでだいたい問題なくなる。