[css+js]スマホサイト用固定ヘッダーとかヘッダーナビについての考察

自作にこだわらずにjQueryプラグイン使えよって話だけどね。
まあ色々と。

作ったもの

無題ドキュメント

仕様

  • ヘッダー固定
  • 多階層OKなアコーディオン式ナビゲーション
  • 画面ぶち抜いても平気なナビゲーション
  • ナビゲーション表示時スクロール禁止
  • 下にスワイプしたらヘッダー隠す
  • 上にスワイプしたらヘッダー表示

問題点

飾ってもしょうがないので先に書いておく。
スワイプによるヘッダーの挙動だけど、iOS(Android未検証)環境で閲覧すると、
上なり下なりページの端までぐいっとすると領域外まで表示して戻ってくるバウンススクロールとやらがあるんだけど、
その跳ね返りまで認識して「上にスワイプしたのにヘッダーが引っ込む」「下にスワイプしたのにヘッダーが出てくる」
ってことが発生する。

無効化するか無視するかしないとどうにもならないんだけど、
その方法がわからないまま。
一応調べてみたんだけど。
『html{overflow:hidden;}body{position:absolute;云々}』をやるとブラウザ自体の上下のアレが引っ込まなくなって具合が悪いとか、なんかもう。
解決前に疲れてしまってどうしようもない状態。

20180209:どうにかなったので最後に追加。

ソース

css
js
記事に貼り付けても圧迫しかしないので。

css

ヘッダー、ナビゲーション、ナビゲーション開閉ボタンをそれぞれposition:fixed;で絶対値指定してる。
ボタンについては最初なんとなくだったけど、ヘッダー引っ込めても残るのはちょっとかっこいいかなって思った。

ナビゲーションの開閉

非表示時は『right:-100%;』で画面外に追いやり、表示のとき『right:0;』にする。
『transition: all 0.4s;』で右から出てくるアニメーションを付ける。
座標は『.close』の有無でそれぞれ指定し、開閉ボタンのクリックをトリガーにjsで切り替えをしてる。
rightの数値もjsで幅取得するつもりだったんだけど、100%で取れるのは収穫だった、知らなかった、やってみるもんだ。

ナビゲーションの表示領域

ただ羅列しても、項目が多いと画面をぶち抜いてしまう可能性がある。
それで固定表示だとスクロールができないのでリンクが死んでしまう。
なので、ulの親要素navに『overflow-y: auto;』を付けて、要素内でスクロールするようにした。
高さについては『height: calc(100% – 50px);』で表示領域からヘッダー分を引いて、ちょうど収まるようにしてある。

js

多階層対応アコーディオン

いつだったかに書いた記事から更にグレードアップした感じ。
ネックは、トリガーがaタグでいけるところ。
[js title=”多階層対応アコーディオン”]
var subnavBtn = $(‘li.subnav > a’);
subnavBtn.click(function(){
event.preventDefault();
$(this).parent(‘li’).toggleClass(‘open’);
$(this).next(‘ul’).slideToggle();
});
[/js]
if文が減らせるのでtoggleって本当に便利ですね。
『event.preventDefault();』でaのリンク挙動を無効化している。
cssでも『pointer-events: none;』があるけど、これだとaに付与したjsも無効化してしまう。
何でaでやってるかっていうと、wpのメニューで階層作ったのをそのままやれるようにしたかったから。

スワイプによるヘッダーの表示・非表示

広告バナーとかを画面下部に固定表示させることって地味にある。
ヘッダーを固定表示した状態でそれがあると、画面が狭くなってしまう。
スマホが大画面になってきてるとはいえ、圧迫感がしんどいので下にスワイプする(≒読み進める)ときは非表示、
上にスワイプ(≒上部コンテンツに戻る)ときに表示させるようにしたかった。
出入りのアニメーションさせてるのは趣味。
[js title=”ヘッダーの表示・非表示”]
var start_pos = 0;
$(window).scroll(function(){
if(flag === true){
var current_pos = $(this).scrollTop();
if (current_pos > start_pos) {
console.log(start_pos);
$(‘header’).stop().animate({top:”-50px”},500);
} else if(0 < current_pos) { $('header').stop().animate({top:0},500); } start_pos = current_pos; } }); [/js] flagのif文で包んであるのは、下記のナビゲーション表示時にも『画面固定しててもスワイプ判定読んじゃう』症状を回避させるため。 スクロール時に座標を読み取って数値の増減で動作を割り振ってる。 elseじゃなくてelseifにしてるのは、elseだと読み込み時にヘッダーが隠れちゃうのを回避するため。 スクロールが発火タイミングのはずなのになんでだろうね。 ナビゲーションと違ってjs上で動作を付けてるのは、 引用元のがそのままいい感じにできちゃったから弄るのだるくてそのままにしてるせい。 本当ならここで更に、上記のバウンススクロールが反応しちゃってるので、
それをどうにかするをしないといけない。

ナビゲーション表示時スクロール禁止

[js title=”ナビゲーション表示時スクロール禁止”]
var scrollTop = $(window).scrollTop();
if(headerNav.hasClass(‘close’)){
$(‘#wrap’).css({‘position’:’static’,’top’:’0′});
$(‘html,body’).scrollTop(scrollTop);
flag = true;
}else{
$(‘#wrap’).css({‘position’:’fixed’,’top’:-scrollTop});
flag = false;
$(‘header’).stop().animate({top:0},500);
}
[/js]
ナビゲーションに合わせてヘッダーも表示するようにしてある。
表示させないとマヌケな感じになる。
ページ自体をスクロール禁止にするとナビゲーションもだめになっちゃうので、
それ以外(#wrap)をスクロール禁止に指定してある。

セレクタとして複数の変数を指定する

小ネタ。
[js title=”変数セレクタ複数指定”]
$.merge(headerNavBtn,headerNavBtnClose).click(function(){
[/js]
『$.merge(【変数】,【変数】)』って書く。
今回はナビゲーションの開閉ボタンが複数あるので使用した。

【20180209追記】バウンススクロール対策

[js title=”バウンススクロール対策”]
var browserTop = 0;
var browserBottom = window.innerHeight ? window.innerHeight: $(window).height();
browserBottom = $(‘body’).height() – browserBottom;
if(current_pos < browserTop){ current_pos = 0; }else if(current_pos >= browserBottom){
current_pos = start_pos + 1;
}

[/js]
バウンススクロールが発生するとき、.scrollTop()は本来の最大・最小値を超えた数値を取得する。

上部対策

バウンススクロール発生時、『マイナス値→0』になるため、数値が増えたと判断されてヘッダーが表示される。
それを回避するため、マイナス値を拾ったら強制的に0になるように指定する。

下部対策

一番下までスクロールしたときの.scrollTop()は、『コンテンツの高さ-画面の高さ』になる。
環境によって画面の高さは変わるし更新によりコンテンツの高さも変わるのでそれぞれを計測し、最下部の数値を出す。
バウンススクロールが発生すると『バウンススクロール値 > コンテンツの高さ』で数値が減ったと判断されるので、
強制的にスクロール前よりも大きい数値になるように指定する。

まとめ

バウンススクロールがなければ何も問題ないのに。
何なのこれ本当に。

バウンススクロール対策は一晩寝て起きた時にひらめいた。
してやったり。

20180216追記

ナビ表示時にスクロール無効にしたこの処置、
ページ内リンクが死ぬ事を確認。

コメント

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