0

I'm detecting device orientation using window.matchMedia. The following code works as intended - every orientation change is logged to console with correct value:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Test App</title>
  </head>
  <body>
    <script type="application/javascript">
        let isIframe= () => {
            try {
                return window.self !== window.top;
            } catch (e) {
                return true;
            }
        }    

         let onOrientationChange = () => {
            const isLandscape = window.matchMedia("(orientation: landscape)").matches;
            console.log("Event: " + (isIframe() ? "Iframe " : "") + "landscape:" + isLandscape);
        }

        let mediaQueryList = window.matchMedia("(orientation: landscape)");
        
        console.log("Onload: " + (isIframe() ? "Iframe " : "") + "landscape:" + mediaQueryList.matches);

        mediaQueryList.addListener(onOrientationChange);
    </script>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root">Hello World in Iframe</div>
  </body>
</html>

But when I run that page in iframe, callback registered using addListener is not fired. In iframe, I only get singular log line - Onload: Iframe landscape:true, regardless of the device orientation.

 <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root">Hello World</div>
    <iframe id="game" src="iframeContent.html" frameborder="0" style="width: 960px; height: 600px;"></iframe>
  </body>

I'm using addListener instead of addEventListener, because the second one function is not working on all Safari versions.

Tested on Safari 14 and on Dev Tools of Chrome and Firefox.

My question is - why addListener callback is not invoked in iframe.

Thank you.

sgnsajgon
  • 664
  • 2
  • 13
  • 56

1 Answers1

1

If the iframe does not get it's size to change because it has fixed width and height, thus resize related events cannot be triggered inside it including MediaQueryList events regarding orientation.

You can do two things to get this working; you can make your iFrame width and height to be 100%, or you can let the media query detection code inside the main window and pass the orientation state using postMessage when it triggers a change event.

1) Changing iFrame size to 100% so it resizes when landscape/portrait orientation event triggers

In the main page, make the body full height and the iframe full width/height (using CSS).

body {
  height: 100vh;
  margin: 0;
}

iframe {
  width: 100%;
  height: 100%;
}

Live example that you can test: https://zikro.gr/dbg/so/65704468/

2) Media query detection on the main page and use postMessage to send a message to iFrame when orientation event triggers

index.html:

<iframe src="iframe.html"></iframe>
<script>
  let iframe = document.querySelector('iframe');

  let onOrientationChange = () => {
    iframe.contentWindow.postMessage({
      isLandscape: window.matchMedia("(orientation: landscape)").matches
    }, '*');
  }

  iframe.addEventListener('load', onOrientationChange);

  const mediaQueryList = window.matchMedia("(orientation: landscape)");

  mediaQueryList.addListener(onOrientationChange);
</script>

iframe.html:

<script>
  window.addEventListener("message", (event) => {
    if (event.data.isLandscape) {
      console.log('iFrame Landscape');
    } else {
      console.log('iFrame Portrait');
    }
  });
</script>

Live example that you can test: https://zikro.gr/dbg/so/65704468/pm/

Christos Lytras
  • 36,310
  • 4
  • 80
  • 113