Safariでvideoのmedia判定が不安定になる原因と対処

Safariでvideoのmedia条件によるsourceの出し分けが、意図通りに動作しないケースがありました。

原因は実装ではなく、Safariの判定タイミングによるものだと思います。

本記事では、その原因と対処方法をまとめます。

目次

問題の構成

今回問題となった動画はメインビジュアルで使用されており、780px以上の時はPC動画、それ以外の時はSP動画が表示されます。

<video autoplay muted loop playsinline>
  <source src="../assets/images/pc/mv.mp4" media="(min-width: 780px)" type="video/mp4">
  <source src="../assets/images/sp/mv.mp4" type="video/mp4">
</video>

しかし、Safariではmedia属性の条件が適用されず、常にSP動画(デフォルトのsource)が再生されてしまいます。

リロードで稀に正常になることがあるものの安定はしません。

原因の特定

headタグ内はシンプルで、CSSファイルとJSファイルを1枚ずつ読み込んでいるだけです。

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title></title>

  <!-- css -->
  <link rel="stylesheet" href="../assets/css/style.css">

  <!-- js -->
  <script src="../assets/js/main.js" defer></script>
</head>

検証を進める中で、scriptタグのdefer属性を外すと、正常に表示されるケースが増えることがわかりました。

おそらくこう

Safariの内部挙動

  1. HTML解析開始
  2. video発見
  3. 即座にsource選択を開始(←ここが早すぎる?)
  4. まだCSSやviewport完全確定してない
  5. media判定が曖昧 → SP動画を選択

なぜdeferを外すと改善するか

  • scriptが解析中に実行される
  • video評価との順序が変わる
  • 結果として正しく判定されるケースが増える

改善案

JSで制御する

フォールバックとして、JavaScriptでvideoのsourceを切り替えます。

// Safariでvideoのmedia判定が不安定なため、JSで動画を明示的に切り替える
const v = document.querySelector('.mv video');
const mq = matchMedia('(min-width: 780px)');

if (v) {
  const set = () => {
    v.src = mq.matches
      ? '../assets/images/pc/mv.mp4'
      : '../assets/images/sp/mv.mp4';
    v.load();
  };

  set();
  mq.addEventListener('change', set);
}
  • load()で動画の再読み込みを明示的に行う
  • changeイベントで画面幅変更にも対応

CSSで切り替える

メディア属性を使わずCSSで動画を切り替える方法です。

<div class="mv">
  <video class="is-pc" autoplay muted loop playsinline src="../assets/images/pc/mv.mp4"></video>
  <video class="is-sp" autoplay muted loop playsinline src="../assets/images/sp/mv.mp4"></video>
</div>
.is-pc { display: none; }
.is-sp { display: block; }

@media (min-width: 780px) {
  .is-pc { display: block; }
  .is-sp { display: none; }
}

複数の動画を同時に読み込むためパフォーマンスは落ちますが、JS無効環境でも確実に切り替えが可能です。

補足:リロードによる対応

※上記のアプローチを採用している場合は不要です。

videoタグはページ読み込み後に画面幅が変わってもsourceを切り替えてくれないので、タブレットの向き変更などで表示が崩れることがあります。

例:PCは横長動画、スマホでは縦長動画など

そういったケースでは、画面幅の変化を検知してリロードさせるのが良いかもしれません。

// 特定の画面幅でリロード(video sorce切り替えのため)
const mq = window.matchMedia("(min-width: 780px)");

mq.addEventListener("change", () => {
  location.reload();
});

Safariのバグ?

最終的に解決はできたものの、根本的な原因は不明なため、少しモヤッとしてしましますね。

一応記録として残しておきます。

カスタマイズに困ったらお気軽にご相談を!

  • 「ちょっとしたCSSの調整だけお願いしたい」
  • 「不具合を直してほしい」

料金は3,000円〜、お支払いは銀行振込・Amazonギフトカードなど柔軟に対応してます🤔

役に立ったら他の方にシェア

お気軽にコメントどうぞ

コメントする

目次