IOS – fixes

iOS / Apple Rules That Must Never Be Broken

These are enforced at the OS level — break them, and you get native fullscreen + broken custom UI.
Which is actually what is happening on mobile.

CALLWhat it does for us vs. apple
playsinlinePrevents forced fullscreen in iOS Safari
webkit-playsinlineLegacy support for older Safari versions
mutedAllows autoplay on mobile
controlsMust be included for initial rendering; Video.js will override
Use setControls(false)Disables iOS native control overlay after load
Avoid autoplay without mutedSafari won’t allow it inline
Don’t rely on disabling fullscreen via JSiOS ignores it; only inline playback prevents it


What we have to do

  1. Get a static ID / data-attribute on the player (or by studio) so that we can targget it easily – and get it set up to work after the plugins have run
  2. Use the correct player options to disable native iOS behaviors (do what they want or they take over with THEIR player)
  3. Add playsinline, muted, and control bar – necessary attributes to the “video” tag (for IOS)
  4. Clean up any injected tracks and ensure plugin timing works
  5. Work changes into the templlate – so it lasts.

Setting static ID / data-attribures (call from head)


<script>
  document.addEventListener("DOMContentLoaded", function () {
    setTimeout(() => {
      Object.values(videojs.players).forEach(player => {
        player.ready(function () {
          const el = this.el();

          // Add a static class - maybe per studio?
          el.classList.add('js-video-ready');

          // Add a stable data attribute for future targeting
          el.setAttribute('data-player-id', 'main-video');

          // Optional: Tag the <video> element itself too
          const tech = el.querySelector('video.vjs-tech');
          if (tech) {
            tech.setAttribute('data-player-id', 'main-video');
          }

          // Later you can use:
          // document.querySelectorAll('[data-player-id="main-video"]')
        });
      });
    }, 300); //wait for everyting to initialize
  });
</script>


In the video player itself – we need to make apple happy so it doesnt’ decide to TAKE OVER the player.

  • Use muted, playsinline, and webkit-playsinline
  • Call setControls(false) via player.tech() after .ready()
  • you can’t stop it with = player.exitFullscreen() — it only works after native fullscreen triggers

If you skip any one of these, Safari often falls back to native fullscreen.

 

Player code in backend

<video
id = "video_<?=$set['Id']?>"
class="video-js vjs-fluid"
playsinline 
webkit-playsinline <!--to keep ios from getting it fullscreen and tossing away videojs-->
 muted <!--to follow apples rules-->
preload="auto"
crossorigin = "anonymous"
controls
poster = "<?=$poster_img?>"
data-setup="{}"
>
<source src="<?=$trailer_file?>" type="application/x-mpegURL">
</video>


Should make this on front end:
which does not “break” any of apples rules – and triggers the IOS native player in safari:


    <video id="video_<?=$set['Id']?>" class="video-js vjs-fluid" muted playsinline webkit-playsinline controls preload="auto" poster="<?=$poster_img?>" >
  <source src="<?=$trailer_file?>" type="application/x-mpegURL">
</video>    <videoid="video_<?=$set['Id']?>" class="video-js vjs-fluid" muted playsinline webkit-playsinline controls preload="auto" poster="<?=$poster_img?>" >
  <source src="<?=$trailer_file?>" type="application/x-mpegURL">
</video>


Cleanup the text-track and more no IOS settings

this repalces what we have for the player creation: var player = videojs('video_', {}); (where you call the player up)

<script>
function initVideoPlayer(videoId) {
  const player = videojs(videoId, {
    html5: {
      nativeTextTracks: false,
      nativeControlsForTouch: false
    },
    controlBar: {
      fullscreenToggle: true
    }
  });

  if (player.nuevo) {
    player.nuevo({
      shareMenu: false,
      settingsButton: true,
      rateMenu: false,
      zoomMenu: false,
      qualityMenu: true
    });
  }

  player.ready(function () {
    player.tech().setControls(false);

    player.one('loadedmetadata', function () {
      Array.from(player.textTracks()).forEach(track => {
        const el = track.el || track._el;
        if (el && el.parentNode) {
          el.remove();
        } else {
          track.mode = 'disabled';
        }
      });
    });

    player.el().classList.add('js-video-ready');
  });

  return player;
}

document.addEventListener("DOMContentLoaded", function () {
  initVideoPlayer('video_<?=$set['Id']?>');
});
</script>


Multiple players on same page?

document.querySelectorAll('video.video-js').forEach(el => {
  initVideoPlayer(el.id);
});


AND THE CSS – you already did this I think –

video::-webkit-media-text-track-display {
  display: none !important;
}


Scroll to Top