[CSS] ページ内リンクのスクロール位置のズレをなくす方法
グローバルナビを position:fixed
で画面最上部に固定しているとき、ページ内リンクのスクロール位置がズレて困ったことはありませんか?僕はあります。
CSSで解決する方法を示しますので、ぜひ参考にしてください👍
結論
前衛的な方法と保守的な方法の2つがあります。
前衛的な方法
scroll-padding-top
プロパティ を使えばシンプルに解決できます。
html {
scroll-padding-top: 70px;
}
これだけで、スクロール位置が 70px
ずれてくれます🙌革命的〜!
ただし、2020/05/14時点でIEとSafari(Mac/iOSとも)が非対応なので、特にiPhoneユーザーを意識する場合は採用するのが難しいのが現状です。
保守的な方法
scroll-padding-top
を使わない場合、以下のようなCSSで解決できます。
body {
padding-top: 70px;
}
h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
position: relative;
}
h1[id]:before, h2[id]:before, h3[id]:before, h4[id]:before, h5[id]:before, h6[id]:before {
content: '';
display: block;
height: 70px;
margin-top: -70px;
}
なお、下図のように見出しタグの
hover
でリンクを表示させたい場合の当たり判定の調整方法については こちらの記事 で解説していますので、よろしければあわせてご参照ください。
移行は、こちらの方法について詳細に解説します。
具体例
一応、よく分からないという人のために具体例を示しながら順を追って説明してみます。
position:fixed
なグローバルナビがあると何が起こるか
まず、例として以下のようなBootstrapを使ったHTMLを書いてみましょう。(コピペでそのまま動かせます)
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-dark bg-dark fixed-top">
<div class="container">
<a class="navbar-brand" href="">Navbar</a>
</div>
</nav>
<div class="container">
<p>先頭のコンテンツ</p>
</div>
</body>
</html>
fixed-top
クラスを指定しているので、このnavbarは position:fixed
でページ最上部に固定されます。
これをブラウザで見てみると、以下のような表示になります。
先頭のコンテンツ
という文字が見当たりませんね🤔
デベロッパーツールでnavbarを削除してみましょう。
navbarの下に隠れていました。
つまり、 position:fixed
なグローバルナビがあると、bodyの先頭部分が隠されてしまうというわけです。
padding-top
を設定して解決
先頭が隠されてしまう問題を、bodyに この問題を解決するには、以下のようにbodyに padding-top
を設定してあげればよいです。
body {
padding-top: 70px;
}
この例で設定した 70px
という数字は、navbarの高さ 56px
にいくらか足して先頭に適度に空白ができるように調整したものです。デザインに応じて適当に設定しましょう。
padding-top
があっても、ページ内リンクのスクロール位置は変わらない問題
bodyに さて、このページにページ内リンクを導入したいとしましょう。
HTMLを以下のように修正して、 id="h1"
を設定した見出しの位置までページ内リンクできるようにしてみます。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<style>
body {
padding-top: 70px;
}
</style>
</head>
<body>
<nav class="navbar navbar-dark bg-dark fixed-top">
<div class="container">
<a class="navbar-brand" href="">Navbar</a>
<ul class="navbar-nav mr-auto">
<li class="nav-item"><a href="#h1" class="nav-link">見出しへジャンプ</a></li>
</ul>
</div>
</nav>
<div class="container">
<p>先頭のコンテンツ</p>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<h1 id="h1">見出し</h1>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>
</body>
</html>
動かしてみると…
見出し
が表示されません。
navbarを消してみると、
案の定、下に隠れていました。
考えてみれば当たり前のことですね。bodyの padding-top
によってページの先頭にはnavbarの分だけ余白を作りましたが、ページ途中の要素の位置まで飛んだときにはその要素が画面の最上に来る位置がスクロール位置になるからです。
見出しの上に見えない余白をつければ解決できる
このページ内リンクのスクロール位置ズレ問題、jQueryプラグインなどでページ内リンク時にちょっとだけ余分にスクロールしてくれるようにするといった解決法も考えられますが、実はCSSだけで対応できますよというのがこの記事の内容です。
最初に書きましたが、以下のCSSで解決できます。
body {
padding-top: 70px;
}
h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
position: relative;
}
h1[id]:before, h2[id]:before, h3[id]:before, h4[id]:before, h5[id]:before, h6[id]:before {
content: '';
display: block;
height: 70px;
margin-top: -70px;
}
id
が設定されているすべての見出しタグに対し、 70px
分の「見えない余白」を作っています。
これにより、ページ内リンク時のスクロール位置が「見えない余白の先頭」になるので、上手くnavbarからはみ出てくれるというわけです。
動かしてみましょう。
バッチリですね!
まとめ
- グローバルナビを
position:fixed
で画面最上部に固定しているときにページ内リンクのスクロール位置がズレる問題はCSSだけで解決できる
Discussion