iPhoneでもスムーズに、一定量スクロールしたら追従固定になるグローバルメニューを実装する

最終的な一応の完成コーディング例

DEMO

 

よくある、グローバルメニューがウインドウの上部に来たら追従固定になるようなものを実装します。

コーディング例1
DEMO 1

<div id="header">HEADER</div>
<div id="menu">
	<ul>
		<li><a href="/link1/">LINK 1</a></li>
		<li><a href="/link2/">LINK 2</a></li>
		<li><a href="/link3/">LINK 3</a></li>
		<li><a href="/link4/">LINK 4</a></li>
	</ul>
</div>

~以下、略~
#menu {
	height: 50px;
}
#menu ul {
	display: table;
	width: 100%;
}
#menu ul li {
	display: table-cell;
	border-right: 1px #fff solid;
}
#menu ul li:last-child {
	border-right: none;
}
#menu ul li a {
	display: block;
	text-decoration: none;
	font-size: 13px;
	line-height: 50px;
	color: #fff;
}

このようなwebサイトだったとして、
#menuの位置までスクロールされたら子要素のulに「fix」クラスを付与して「position: fixed;」にします。

#menu ul.fix {
	position: fixed;
	top: 0;
	left: 0;
	z-index: 800;
}
$(function(){
	// #menuのデフォルト位置を取得
	var ps = $('#menu').offset().top;

	$(window).on('load scroll resize', function(){
		// 現在のスクロール量の取得
		var wS = $(window).scrollTop();

		// #menuの位置よりスクロール量が多ければ「fix」を付与
		if(wS > ps) {
			$('#menu ul').addClass('fix');
		} else {
			$('#menu ul').removeClass('fix');
		}
	});
});

PC閲覧のみならこれでOKです。
メニューが「position: fixed;」になると要素の高さ判定がなくなりますので、親要素「#menu」に高さを指定しておくことを忘れずに。

iPhoneでの問題

ここで問題なのがスマホでの閲覧の場合、上記のやり方だと、スクロールが終わったタイミングで実行されるため、スムーズに追従固定に切り替わりません。

その対策としてやり方を変えたものが下記。

コーディング例2
DEMO 2

<div id="menu">
	<ul>
		<li><a href="/link1/">LINK 1</a></li>
		<li><a href="/link2/">LINK 2</a></li>
		<li><a href="/link3/">LINK 3</a></li>
		<li><a href="/link4/">LINK 4</a></li>
	</ul>
	<ul class="fix">
		<li><a href="/link1/">LINK 1</a></li>
		<li><a href="/link2/">LINK 2</a></li>
		<li><a href="/link3/">LINK 3</a></li>
		<li><a href="/link4/">LINK 4</a></li>
	</ul>
</div>
#menu ul.fix {
	visibility: hidden;
}
#menu ul.fixed {
	visibility: visible;
}

先ほどの例から、#menuの子要素ulをもう一つ用意して「fix」クラスを付与して「visibility: hidden;」にしておきます。
追従固定になるタイミングで「fixed」クラスを付与して「visibility: visible;」に切り替え。

$(function(){
	// #menuのデフォルト位置を取得
	var ps = $('#menu').offset().top;

	$(window).on('load scroll resize', function(){
		// 現在のスクロール量の取得
		var wS = $(window).scrollTop();

		// #menuの位置よりスクロール量が多ければ「ul.fix」に「fixed」を付与
		if(wS > ps) {
			$('#menu ul.fix').addClass('fixed');
		} else {
			$('#menu ul.fix').removeClass('fixed');
		}
	});
});

これで完成。
と思いきや、このやり方を実装したサイトの下層ページでは、DEMO 1 と同様に、スクロールが終わったタイミングでしか切り替わらない事象が発生。

コーディング例3
DEMO 3

色々調べてみた結果、トップページにはbxSliderを使ったスライダーがあり、それのおかげでたまたま成功していただけだということが判明。
どうやら、スライダーのエフェクトのためにbxSliderが勝手に付与してる「transform: translate3d(-290px, 0px, 0px);」があることで、追従固定メニューもスムーズに動いていた模様。
試しに#menuに「transform: translate3d(0px, 0px, 0px);」って入れたらスムーズに動きました。

でもこれじゃ何か気持ち悪いし無駄なCSS追加したくないので#menuに「will-change: transform;」を指定。
https://developer.mozilla.org/ja/docs/Web/CSS/will-change
これは何か、グラフィックアクセラレーションとかいうやつを使いますよって宣言してるやつみたいですが、色々奥が深そうなのでまた折を見て調べてみようかと思います。

成功コーディング例
DEMO 4

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です