Closed6

JavaScript によるデバイスの判定のあれこれ

jambandjamband

CSS の hover のスタイルをタッチデバイスとその他で分けたかったので、デバイスの判定についていろいろ調べる。

jambandjamband

役に立ったリンク

繰り返します。ユーザーエージェントを調べるのが良いことはめったにありません。問題を解決するには、もっと良い、もっと広く互換性のある方法が見つかるはずです。

という注意書きがされているように、ユーザーエージェントによるデバイスの判定はできる限り避けて、他のアプローチでやったほうがいいよ、と。

jambandjamband

すべて上記のリンクに書かれているが、結果として以下のような関数が出来上がった:

src/utils/screen.ts
export const hasTouchScreen = () => {
  if (navigator.maxTouchPoints > 0) {
    return true;
  }
  if (navigator.msMaxTouchPoints > 0) {
    return true;
  }
  if (window.matchMedia("(pointer:coarse)").matches) {
    return true;
  }
  if ("orientation" in window) {
    return true;
  }

  return false;
};

React Hook 化した場合は以下:

src/hooks/screen.ts
import { useEffect, useState } from "react";
import { hasTouchScreen } from "../utils/screen";

export const useHasTouchScreen = () => {
  const [state, setState] = useState(false);

  useEffect(() => {
    setState(hasTouchScreen());
  }, []);

  return {
    hasTouchScreen: state,
  } as const;
};

コンポーネントで使う場合は以下:

src/pages/foo.tsx
import Link from "next/link";
import { useHasTouchScreen } from "../hooks/screen";
import styles from "./foo.module.scss";

const View: React.VFC = () => {
  const { hasTouchScreen } = useHasTouchScreen();

  return (
    <>
      <Link href="/">
        <a className={`${!hasTouchScreen && styles.link}`}>Home</a>
      </Link>
    </>
  );
};

export default View;
src/pages/foo.module.scss
.link {
  &:hover {
    color: green;
  }
}

これで画面をタッチで操作できないデバイスのみ hover すると Home リンクの文字が緑色になる。

jambandjamband

画面をタッチで操作できるデバイスの判定をしている関数について詳しく見ていく。

export const hasTouchScreen = () => {
  if (navigator.maxTouchPoints > 0) {
    return true;
  }
  if (navigator.msMaxTouchPoints > 0) {
    return true;
  }
  if (window.matchMedia("(pointer:coarse)").matches) {
    return true;
  }
  if ("orientation" in window) {
    return true;
  }

  return false;
};

MDN Web Docs の説明によると以下のように書かれている。

Navigator インターフェイスの maxTouchPoints プロパティは読み取り専用で、現在の端末で対応している同時タッチコンタクト点の最大数を返します。

若干説明が分かりにくいが、画面をタッチで操作できるデバイスならば最低でも 1 になるので、それを判定する、という感じ。だいたいがこれでいけそう。

例外として、msMaxTouchPoints というのがあるらしく、これについては調べても良い情報が出てこなかった。おそらく Windows 系の何かだろう。

window.matchMedia("(pointer:coarse)").matches

pointer の coarse というのは MDN Web Docs の説明によると

主要な入力メカニズムにポインティングデバイスがありますが、その正確性が限定されています。

と書かれているが、これはスマホやタブレットだと画面のタッチによる操作を指などでやるのでやや荒くなり、pointer の判定が coarse になるんだろうか。詳しくはわからない。

"orientation" in window

orientation というのは MDN Web Docs の説明によると

orientation は Screen インターフェイスの読み取り専用プロパティで、現在の画面の向きを返します。

と書かれており、スマホやタブレットだと縦画面や横画面であーだこーだできるので、それを「画面をタッチで操作できるデバイス」の判定でも利用できる、ということなんだろう。ただ、Screen.orientation は実験的な機能ですべてのブラウザには対応していないため、判定の優先度としては最後のほうに書くことになる。

このスクラップは2021/12/31にクローズされました