1
1

More than 3 years have passed since last update.

iOSでWebページの一部要素のみスクロール可能にする

Last updated at Posted at 2020-06-13

背景

PC や Android など、iOS 以外のデバイスで、ページ全体をスクロール無効にし、それ以外の要素は有効にしたい場合は、以下の CSS を追加すれば良いです。
(中略)
しかし、iOS では、この方法だとうまくいきません。

実現したいこと

ページ全体をスクロール禁止にした上で、スクロール可能な要素についてのみ内部の(子要素を含む)どこをタッチしてもスクロールする。
overflow:scrollに設定されていても子要素が少なくスクロール可能状態になっていない要素や、スクロール可能状態だが一番下までスクロールされきっている要素はきちんとpreventDefaultしないと上位要素をスクロールしようとしてしまうことに注意する。
(諸事情によりwindowやbodyではなく、自分で作成した一番上位の要素をrootElementとして、これにイベントを設定する仕様になっている)

実現方法

  • 再帰的に親要素を辿ってスクロール可能要素があるか確認する関数を作成

const check = (elem: HTMLElement,rootID:string): boolean => {
  elem.scrollBy({ top: 1 });
  if (elem?.id == rootID) {
    return false;
  } else if (elem.scrollTop) {
    if(elem.scrollTop>1)elem.scrollBy({ top: -1 });

    return true;
  } else if(elem.parentElement){
    return check(elem.parentElement);
  }else{
    return false;
  }
};
  • スクロール可能な要素がなければpreventDefaultする関数を作成
const preventScroll(rootElement:HtmlElement) => {
  rootElement.addEventListener(
    'touchmove',
    e => {
      const elem = e.target as HTMLElement;
      if (!(elem && check(elem,rootElement.id)) && e.cancelable) e.preventDefault();
    },
    { passive: false }
  );
});
  • スクロールを禁止したい要素に適用(ここでは仮称globalContainerに適用)
const root=document.getElementById("globalContainer");
if(root) preventScroll(root)

備考

check()内で一度スクロールを試みた後、スクロール可能な要素と判定された場合最初にスクロールを試みた分を戻しているが、

const check = (elem: HTMLElement,rootID:string): boolean => {
 elem.scrollBy({ top: 1 });
    (中略)
 else if (elem.scrollTop) {
    if(elem.scrollTop > 1) elem.scrollBy({ top: -1 });

    return true;
  } 

これによりスクロール可能要素が最後までスクロールしきった状態にならず、スクロールしきった要素を引っ張って画面全体がスクロールされる仕様に対処している。

まとめ

力押しの判定方法で目標を実現した。
また、e.targetをHTMLElementにキャストしている部分や備考で示した箇所の記述など、確実に正しい記述である自信がない部分も多いため、指摘歓迎。

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1