タブメニューのレスポンシブ対応

 
Web Tips.
Booskanium's
Booskanium's Web Tips.

タブメニューをスマホでも

左右にスワイプできる方法でレスポンシブ対応したタブメニューです。Androidのブラウザで試してみて下さい。

作成理由

タブメニューをスマホでもそのまま使いたいという相談もあり、そのレスポンシブ対応のプロトタイプを試作しました。
要件は
・スワイプで左右に動くタブメニュー
・左右スクロールではない方法
・パソコンでも今まで通り使えること
ことでした。
二番目の理由は後述します。

お手本として着目したのがAndroid版のVivaldiとChromeの開いているタブ切り替えです。

要件からかプロトタイプ試作方針
・タブメニュー左右移動は
 └→transformで
・スワイプはtouchイベントで
・パソコンはmousemoveでスワイプ
 └→マウスドラッグをスワイプもどきに
・パソコンのタッチパネルディスプレイ
 └→現時点では未対応
に決定しました。
因みに今回は対応しないがパソコンのタッチパネルディスプレイはタッチではなく、ポインターイベントで拾うらしい。

左右スライドはtransformで

まず、このページの上段にあるタブメニューの左右移動はtransformのtranslateXで行っています。
このページは圧縮したりしていませんので、詳しくはリバースエンジニアリングしてください。
アプリケーション層でも少し下層レベルな制御を行っていますので奇天烈なコードかもしれません。

浮気でスクロールを試した

要件から外れますが、左右スクロールも試しておきました。打ち込み時間は30分で完成するほど簡単なものです。
しかし、
・CSSでのスクロール非表示はブラウザ依存
・スクロールバーをブロックの外に
いずれも、ブラウザによる違いがあり、スッキリとはなりませんでした。 スクロールバー表示でも良くて、ブラウザごとにスクロールバーデザインが違ってもOKなら、この方法がおすすです。
以下にスクロールで試した結果を表示しておきます。FirefoxとChrome系で比較すると違いがわかります。なおWebkit系のスクロールバー非表示の裏技っぽい情報がググるとヒットしますが、現在のChromeではそれは機能いたしませんでした。

左右のナビ

タブメニューの左右に、左右にスライドさせるナビを設けました。
イメージですがsvgをJavaScript内に抱え込んでいます。
この手法ならcssで色を指定できるので至極便利です。

なお、左右移動が端まで達している場合、または移動できる状態の場合に、このナビの表示を変える事も試しましたがくどくなるだけなので未採用。

SVG部分のコードは

<svg version="1.1" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 515 515">
<g>
<polygon class="petitabArrow02" points="419.916,71.821 348.084,0 92.084,256.005 348.084,512 419.916,440.178 35.742,256.005"></polygon>
</g>
</svg>
です。
“class="petitabArrow02"”部分のクラス属性名はCSSで
.petitabArrow0 { 
	fill: var(--petitTabColorBackground);
}
の様に色を指定する事ができます。

スワイプ

スマホでのスワイプは、以前作成したモジュールを利用しました。
スマホのウェブページでスワイプ
あとで、このモジュールにパソコンのタッチパネルディスプレイ対応も施す予定です。

パソコンの場合はドラッグ(マウス左ボタン押したままでマースを動かす)でスワイプもどきな操作が出来るようにしてあります。

左右のぼかし

タブメニューの左右の端に、メニューが左右に隠れてあるぞという連動が出来るようにした効果です。
レイヤーをかぶせる手法です。 注意としては、上にかぶせますので、CSSに「pointer-events:none;」を指定して、マウスやタッチの操作を下になるレイヤーに伝えることです。

アンカー

タブメニューにアンカーがあると、これが曲者です。
・スワイプのイベントを止めてしまう。
 ├→アンカー部分はURLのドラッグ&ペースト機能
 └→つまりスワイプにバブリングを止めてしまう
・アンカー部分のが選択されてしまう。
これを防ぐために、アンカーのCSSと、アンカー中のてテキストのCSSに「pointer-events:none;」を指定して、実質的にアンカーの機能を無効にしました。そしてJavaScriptでアンカーの動作をさせる様にしました。
アンカーに限らず、スワイプを拾うブロック上にかぶさっている要素には「pointer-events:none;」を指定してくおかないと、面倒な事象に悩むことが解りました。

条件を限定すればまあまあ

・ピンチアウト(ズーム)で拡大するとページ全体の左右移動。
 ├→スクロールなら要素部分のスクロールが優先になっているのに
 ├→つまりviewportで拡大出来ないようにすれば実用的
 └→Chromeがズームを抑止する方法がかなり煩雑でまら対応できていない
・変な所が選択されてしまう。
 ├→CSSの工夫次第だかちょっと難儀
 └→アンカータグとの相性が悪いので工夫が必要
・タブブウ部分のデザインに拘る場合に上記の件で単純じゃない。
・スワイプ検知は別のモジュールを使っている。
 └→
・低階層よりな制御なのでJavaScriptが難解かもです。

上記の理由で、コピペしただけで、はいKですというレベルにはなっていない。

現時点での課題

・スマホのChromeでピンチズームを抑止
現状はターゲットエレメントの時に、マルチタッチだったら抑止した。
下記をトプレベルの中に書いたらOK、これをモジュールの中で考慮するようにする。
document.documentElement.addEventListener('touchstart', function (event) {
if (event.touches.length > 1) {
event.preventDefault();
}
}, false);

・スワイプイベントを拾うのをクラス継承にする。
https://qiita.com/y_catch/items/9349f4b5180c45f73fa7

・スワイプしなくでもタッチエンドで選択解除