今回は特定のエリアで追従するタイトルを実装するコードについて解説していきたいと思います!
追従タイトルの実装といえば、position stickyなどがありますが、親要素や祖先要素にoverflow: hiddenを指定しているとposition stickyが利用できません。
今回はそういった場合に使用できるコードになります!
実際のjavascriptのコードを見てみましょう!
目次
コード
See the Pen 追従タイトル by 八木秀一郎 (@huuglfoo-the-solid) on CodePen.
コードが長いですが、ひとつひとつ解説していきますので、ご安心ください!!
追従させる要素やエリアの指定
const areaFixedFunk = (fixedElm, fixedArea, offsetStart = null, fixedTop = null, offsetEnd = null) => {
const areas = document.querySelectorAll(fixedArea);
if (areas.length === 0) return;
このコードは関数を定義しています。
以下はそれぞれの引数の解説になります!
- fixedElm
追従させる要素(タイトル)を指定します。 - fixedArea
追従が発生するエリアを指定します。 - offsetStart
追従を開始するスクロール位置を指定します。
数値を指定した場合: 追従を開始するスクロール位置を微調整できます。
例:offsetStart = 100 なら、area の開始位置から 100px スクロールされたときに追従処理が開始されます。
null の場合: デフォルト動作(area.getBoundingClientRect().top の値)に依存します。 - fixedTop
追従する要素の top の位置を指定します。
数値を指定した場合: 追従する要素がページの上部からどれだけ離れるかをピクセル単位で指定します。
例: fixedTop = 50 なら、追従する要素は常に上から 50px 離れた位置に配置されます。
null の場合: デフォルト動作(要素のスタイルに依存)に従います。 - offsetEnd
追従を解除する位置を指定します。
数値を指定した場合: 固定解除の位置を指定されたピクセル数分調整します。
例:offsetEnd = 100 なら、エリアの終端から 100px 手前で追従が解除されます。
null の場合: デフォルト動作に従います(area.getBoundingClientRect().top + areaHeight の値)。
追従させるために必要な各値の取得
const checkFixed = (target, area) => {
const startPosi = area.getBoundingClientRect().top;
const targetHeight = target.clientHeight;
const areaHeight = area.clientHeight;
const areaLeft = area.getBoundingClientRect().left;
checkFixed 関数の引数から解説していきます。
- target
追従の対象となる要素。fixedElmで指定した要素です。 - area
追従が発生するエリア。fixedAreaでで指定した要素です。
次は各定数がどういった意味を持つのか見ていきましょう!
- startPosi
値:area.getBoundingClientRect().top;
エリアの上端の位置を ビューポートの上端からの距離です。 - targetHeight
値:target.clientHeight;
追従する要素(target)の高さです。 - areaHeight
値:area.clientHeight
追従が発生するエリア全体の高さです。 - areaLeft
値:エリアの左端の位置を ビューポートの左端からの距離 で取得しています。
これにより、ウィンドウサイズに依存せず正確に水平位置を固定できます。
条件分岐を使用してクラスの付与と追従させたい要素の位置調整
if (startPosi <= offsetStart && (startPosi + areaHeight) > offsetStart + targetHeight + offsetEnd) {
target.classList.add(‘is-fixed’);
target.style.top = fixedTop + ‘px’;
target.style.left = areaLeft + ‘px’;
} else if (startPosi > offsetStart) { target.classList.remove(‘is-fixed’);
target.style.top = ”;
target.style.left = ”;
} else {
target.classList.remove(‘is-fixed’);
target.style.top = (areaHeight – targetHeight) + ‘px’;
target.style.left = ”;
}
このコードでは追従させる要素の状態を判定し、条件に応じてクラスの付与と削除を行っているコードになります。
では最初のif文について解説します!
条件式(追従状態)
- startPosi <= offsetStart
エリアの上端位置(startPosi)が追従開始位置(offsetStart)に達した場合。 - (startPosi + areaHeight) > offsetStart + targetHeight + offsetEnd
追従要素がエリアの範囲内に収まっている場合。
動作
- クラスの付与:
is-fixed クラスを追加して、固定表示の状態に変更します。 - スタイルの設定
top: 要素をfixedTopで指定した位置(ビューポート上端からの距離)に配置します。
left: エリアの左端(areaLeft)に合わせて水平位置を固定します。
続いてはelseif の条件式です
条件式(追従開始前)
- startPosi > offsetStart
エリアがまだ追従開始位置(offsetStart)に達していない場合。
動作
- クラスの削除:
is-fixed クラスを削除して、追従状態を解除します。 - スタイルの初期化
top と left を空に設定して、要素を元の位置に戻します。
最後にelseの条件を詳しく見ていきましょう。
条件式(追従を解除)
- 追従要素の下端がエリアの終端を超えた場合。
動作
- クラスの削除:
追従要素をエリア内の終端に固定するために、(areaHeight – targetHeight) の位置に配置します。 - スタイルの初期化
top::追従要素をエリア内の終端に固定するために、(areaHeight – targetHeight) の位置に配置します。
left: 水平方向の固定は解除します。
このようにこのコードでは、追従された際にクラスの付与と削除を行い、cssのスタイルを調整しています!
画面幅に応じて追従を解除
const handleScrollOrResize = () => {
areas.forEach((area) => {
const target = area.querySelector(fixedElm);
if (target && window.innerWidth >= 0) {
checkFixed(target, area);
} else {
target.classList.remove(‘is-fixed’);
target.style.top = ”;
target.style.left = ”;
}
});
};
handleScrollOrResize();
window.addEventListener(‘resize’, () => { handleScrollOrResize(); });
window.addEventListener(‘scroll’, () => {
if (!ticking) {
window.requestAnimationFrame(() => {
handleScrollOrResize(); ticking = false;
});
ticking = true;
}
}, { passive: true });
長々と書いていますが、ここまでくれば後は簡単です!
簡潔に説明しますね!
このコードはスクロールやリサイズに応じて、追従要素の状態を更新する関数です。
今回のコードではwindow.innerWidth >= を0に設定していますが、
仮に600を設定した場合、画面幅が600px以上の時のみ要素が追従し、それ以下では追従しないようになります。
デザインの都合上、画面幅が狭いときに追従を解除したい場合などに、活用することができます!
CSS
.fixedArea {
position: relative;
}
.fixedTitle {
position: absolute;
}
.fixedTitle.is-fixed {
position: fixed;
}
- .fixedArea
fixedTitleがfixedAreaを基準に配置されるようにしています。 - .fixedTitle
fixedAreaの終端に達した際に、終端で固定するために指定しています。 - .fixedTitle.is-fixed
スクロール時にfixedArea内で追従させるために必要です。
上記はCSSのスタイリングになります。
これを記載すればタイトルが追従するようになるはずです!
まとめ
今回は、特定のエリアで追従するタイトルを実装するコードについて解説しました!
position: stickyが使えない状況でも、position: fixedや条件分岐を活用することで、追従処理が可能になります。
デザインの一部を追従させたい場面や、特定のエリア内だけで表示位置をコントロールしたい場面で、このコードは役に立ちます!
よろしければ実装して試してみてください!