スプリットレイアウトって?
スプリットレイアウトとはSplit(分割)の通り、画面を2カラム、あるいは3カラムなどに分割した画面レイアウトのことです。
パッと思いつくのは2カラムで片方は固定されていて、もう片方はスクロールが可能なデザインかなと思います。
最近だとスマホファーストが主流になっているのもあって、PCで閲覧した際にメインのコンテンツはスマートフォンのレイアウトになっていて、これを中央に配置しつつ、左右はメニューや読ませたい文章、写真なんかが固定されているのも結構見るな~という気がします。
参考までに、こういうサイトのことです。
スプリットレイアウトについていろいろ調べていたら2017年に流行して、2019年頃にも再熱、そして昨今でもよく見かけるデザインの1つなので、Y2Kしかり流行って巡るものですね。
こういうスプリットレイアウトを実装したよ
本題なのですが、今回以下のような仕様のサイトを実装する機会があり、本記事はその備忘録になります。
- 2カラム
- 左側は固定の状態でコンテンツのタイトルが入る
- 右側はスクロールできる状態でコンテンツの詳細が表示される
- フッター上で左側にあるタイトルが固定される
- 右側の次のコンテンツが画面内に表示されたタイミングで、左側のタイトルがフェードイン/アウトで次のコンテンツの内容に切り替わる
文章にすると「ん?」という感じなのですが(私の語彙力が無いからかも…)、要は下記のようなレイアウトのサイトになります。CODEPEN上での閲覧推奨です。
SPでは単純に1カラムのよくあるレイアウトに切り替わるため、今回はSPの記述は割愛しています。
See the Pen Untitled by y-yoshino (@yyko) on CodePen.
仕組みの解説
左側のレイアウトをフェードイン/アウトさせたいため、こういうレイアウトの実装に便利なposition:sticky
は今回は使わないで、左側はposition:fixed
で固定にしつつopacity:0
にして、scriptでのclassの付け替えでフェードイン/アウトを行うようにしています。
右側は単純にmargin-left:auto
で右寄せにしているだけです。
左側のフェードイン/アウトさせるための記述と、フッター上で固定させるための記述はこんな感じです。
/* デフォルト(非表示状態にしておく) */
.js-scroll-element .l-left-content {
cursor: default;
pointer-events: none;
opacity: 0;
transition: opacity 0.7s ease;
}
/* 表示時 */
.js-scroll-element .l-left-content.is-on {
cursor: auto;
pointer-events: auto;
opacity: 1;
}
/* 再度非表示に切り替えるための設定 */
.js-scroll-element .l-left-content.is-off {
opacity: 0;
}
/* フッター上で固定させる */
.js-scroll-element .l-left-content.is-absolute {
opacity: 1;
height: 100%;
pointer-events: auto;
cursor: auto;
position: absolute;
z-index: 1;
}
/* フッター上で固定させた要素内のコンテンツを下付きにする */
.js-scroll-element .l-left-content.is-absolute .l-left-content__inner {
align-items: flex-end;
}
cursor:default
やpointer-events:none
は非表示にしている要素に触れないように設定しています(opacityだけだとあくまでも透過させているだけなので)
上記のclassの付け替えでいい感じにスプリットレイアウトになるようにしています!
続いてJavaScriptの解説です。
何をしているのか少し細かく書いてみました。
ウィンドウの高さとスクロール位置を取得
const windowHeight = window.innerHeight;
const scrollHeight = window.pageYOffset;
フッターを取得
const footer = document.querySelector('.l-footer');
スプリットレイアウトを実装したい要素('.js-scroll-element'
)を取得
const scrollElements = document.querySelectorAll('.js-scroll-element');
ここまでは特に特筆することはないかなという感じです。
.js-scroll-element
に対して繰り返し処理を行う
scrollElements.forEach(element => {
現在のページ内における.js-scroll-element
の垂直位置を計算
const offsetTop = element.getBoundingClientRect().top + window.pageYOffset;
.js-scroll-element
の高さを取得
const height = element.clientHeight;
フェードイン/アウトの処理を行う.l-left-content
(コンテンツのタイトル)を取得
const leftContent = element.querySelector('.l-left-content');
以下で要素の表示状態を処理していきます。
.js-scroll-element
の上部がウィンドウの中間位置よりも上にあり、かつ、.js-scroll-element
の下部がスクロール位置より下にある場合、.l-left-content
を表示状態にする。
if (offsetTop < scrollHeight + windowHeight / 2 && offsetTop + height > scrollHeight) {
leftContent.classList.remove('is-off');
leftContent.classList.add('is-on');
文字にすると大分分かりにくいと思うので、挙動はCODEPENを参照で…(丸投げ)
要は、.js-scroll-element
の要素(というよりかは右側の.l-right-content
の要素で捉えてもらうと分かりやすいかも)がウィンドウの真ん中あたりに来たら.is-on
で表示させて、真ん中よりも下に来たら.is-off
で非表示にしているイメージです。
フッターがスクロール位置の2/3以内に来た場合、要素を固定表示状態にする。
if (footer.getBoundingClientRect().top + window.pageYOffset < scrollHeight + (windowHeight * 2) / 3 && footer.getBoundingClientRect().top + footer.clientHeight + window.pageYOffset > scrollHeight) {
leftContent.classList.remove('is-on');
leftContent.classList.remove('is-off');
leftContent.classList.add('is-absolute');
} else {
leftContent.classList.remove('is-absolute');
}
切り替え用に.l-left-content
に付与させていた.is-on
と.is-off
を外し、position:fixed
を.is-absolute
で設定しているposition:absolute
で上書きし、フッターの上で固定させるようにしています。
そして上記2つの条件に該当していない場合は.l-left-content
を.is-off
で非表示にします。
} else {
leftContent.classList.remove('is-on');
leftContent.classList.add('is-off');
leftContent.classList.remove('is-absolute');
}
});
最後にページの読み込み完了時とスクロール時に、関数を呼び出せばOKです。
document.addEventListener('DOMContentLoaded', setLeftContentVisibility);
window.addEventListener('scroll', setLeftContentVisibility);
最後に
ちょっとリッチ(?)なスプリットレイアウトの実装方法の紹介でした!
似たようなWebサイトを実装する際の参考になれば幸いです
もっと効率の良い書き方があれば是非教えていただけると嬉しいです~