position: fixed;が効かない理由を暴く

HTML/CSSを学ぶ

こんにちは。今回はposition: fixed;が効かない理由とその対処法をまとめていきます。

なぜposition:fixed;が効かないのか | 親要素がtransformしていると効かない

例えばこんなヘッダーを作ろうとしています。こういうヘッダー、コーディングしてると結構出会います。

  • ページ読み込み時にしたからふわっと出てくる
  • グローバルナビがスクロールに追従する

見た目としてはこんな感じです。

さて、これをコーディングすると考えてみます。

まずは見た目はこんな感じでしょう。

<header class="Header" id="Header">
  <nav class="Nav">
    <ul class="List">
      <li class="List-Item"><a href="#">メニュー1</a></li>
      <li class="List-Item"><a href="#">メニュー2</a></li>
      <li class="List-Item"><a href="#">メニュー3</a></li>
    </ul>
  </nav>
</header>
.Header {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 60px;
  background-color: #6ba6e7;
}

.Nav {
  position: fixed;
  top: 18px;
  right: 20px;
}

.List {
  display: flex;
  gap: 20px;
}

.List-Item {
  color: #fff;
  font-size: 24px;
  line-height: 1;
}

アニメーションは以下のような形。

<script
  src="https://code.jquery.com/jquery-3.6.0.slim.min.js"
  integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI="
  crossorigin="anonymous"
></script>

<script>
  window.onload = function () {
    $("#Header").addClass("FadeIn")
  }
</script>
.Header {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 60px;
  background-color: #6ba6e7;
  transition: all 1s; //追加
  transform: translateY(30px); //追加
  opacity: 0; //追加
}

...省略

.FadeIn {
  transform: translateY(0);
  opacity: 1;
}

さて、こうするとなんとposition: fixed;を指定しているはずのグローバルナビ部分がスクロールに追従しません(試してみてください)。

これは親要素(今回は.Header)に、アニメーションのためのtransform: translateY(30px)が付与されているからです。

MDNのpositionプロパティのページ(https://developer.mozilla.org/ja/docs/Web/CSS/position)にも、fixedについて以下のように書かれています。

祖先の一つに transform, perspective, filter の何れかのプロパティが none 以外 (CSS Transforms 仕様書を参照) に設定されている場合は例外で、その場合は祖先が包含ブロックとしてふるまいます。

要するに、親要素にtransformなどの指定があるとposition: absolute;と同様の挙動になってしまうのです。

どう対処するか

transform以外の方法が使えるならそちらを使う

今回であれば.Headerと.Navのtopの値を調整すれば実現可能です。それぞれtranslateYを使った時と同様に最初の値を30pxずらします。

.Header {
  position: absolute;
  top: 30px; //変更
  left: 0;
  width: 100%;
  height: 60px;
  background-color: #6ba6e7;
  transition: all 1s;
  opacity: 0;
}

.Nav {
  position: fixed;
  top: 48px; //変更
  right: 20px;
  transition: all 1s; //追加
  opacity: 0; //追加
}

...省略

//追加
.FadeIn__Nav {
  top: 18px;
  opacity: 1;
}
window.onload = function () {
  $("#Header").addClass("FadeIn")
  $("#Nav").addClass("FadeIn__Nav") //追加
}

これで想定通りの動きになります。

子要素をそれぞれtransformさせる

position: fixed;にしたい要素の親要素にtransformが指定されているのが問題なので、その状態さえ回避すれば良いです。背景とグローバルナビ部分を分け、それぞれをtransformさせます。

<header class="Header" id="Header">
  <div class="Header__Background js-FadeIn"></div> <!-- 追加 -->
  <nav class="Nav js-FadeIn"> <!-- js-FadeInというクラスを追加 -->
    <ul class="List">
      <li class="List-Item"><a href="#">メニュー1</a></li>
      <li class="List-Item"><a href="#">メニュー2</a></li>
      <li class="List-Item"><a href="#">メニュー3</a></li>
    </ul>
  </nav>
</header>
.Header {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 60px;
  //background-colorを削除
}

//追加
.Header__Background {
  width: 100%;
  height: 100%;
  background-color: #6ba6e7;
  transition: all 1s;
  transform: translateY(30px);
  opacity: 0;
}

.Nav {
  position: fixed;
  top: 18px;
  right: 20px;
  transition: all 1s;  //追加
  transform: translateY(30px); //追加
  opacity: 0; //追加
}
<script>
  window.onload = function () {
    $(".js-FadeIn").addClass("FadeIn") //.js-FadeInを対象に変更
  }
</script>

これで想定通りの動きになります。

まとめ

というわけで、position: fixed;が効かない理由と、その対処をまとめました。知らないとかなり対処が難しいので、ぜひ参考にしてください。

コメント

タイトルとURLをコピーしました