Cross Domain IFrame element.scrollIntoView() Safari Issue

后端 未结 2 982
清歌不尽
清歌不尽 2021-01-03 07:56

I have ran into an issue that is plagued all over google but none of the provided solutions work correctly. I assume the majority of these solutions do not account for cross

2条回答
  •  孤街浪徒
    2021-01-03 08:24

    This ended up being a lot more research than code. What was going on was - I had code that resized the IFrame based on the content.

    In all other browsers this works fine and eliminates the scroll bars. Turns out that Safari automatically sizes the Iframe leaving scrolls of it's own. In my application there are zero static pages. This leaves me with the issue of not being able to use the scrolling=no fix described in the link.

    After discovering exactly what was going on I took a different approach to fixing elm.scrollIntoView(). The code is more comments then anything but the important parts are;

    1. Detecting when to apply the Iframe fix with RequiresIframeScrollFix
    2. Using elm.getBoundingClientRect().top to get our scroll position from within the Iframe.
    3. Communicating to the parent to scroll with window.parent.postMessage
    4. Receiving the message in the parent with window.addEventListener('message',...)

    Here's what it looks like.

    Iframe Site

    Our Iframe site currently scrolls it's elements into view like this elm.scrollIntoView(); We've changed that to the following.

    if (RequiresIframeScrollFix())
         window.parent.postMessage(elm.getBoundingClientRect().top, "*"); // Tell IFrame parent to do the scrolling. If this is not a test environment, replace "*" with the parent domain.
     else
         elm.scrollIntoView(); // If not scroll into view as usual.
    

    Optional: fix for bootstrap modal positioning in IOS IFrames using elm.getBoundingClientRect().top.

    $('#modalId').css('top', elm.getBoundingClientRect().top); // This fixes modal not in view on Safari Iframes.
    

    RequiresIframeScrollFix() is mostly made up of some well document code laying around SO to determine if we're in an Iframe on the IPad or IPhone.

    // Used to help identify problematic userAgents.
    var debugNavigator = false;
    
    // Detects an issue on mobile where the Parent is an iframe which cannot have it's scroll bars removed.
    // Presumably not a bug as safari will autosize it's iframes: https://salomvary.com/iframe-resize-ios-safari.html
    // Can use "scrolling=no" fix instead if the parent knows the initial size of your iframe.
    function RequiresIframeScrollFix() {
        try {
            // Debug navigator Agent
            if (debugNavigator)
                alert(navigator.userAgent);
    
            // We know this issue happens inside an IFrame on;
            // Safari iPhone
            // Safari iPad
            // Safari Desktop Works fine.
    
            // Check for safari
            var is_safari = navigator.userAgent.indexOf("Safari") > -1;
            // Chrome has Safari in the user agent so we need to filter (https://stackoverflow.com/a/7768006/1502448)
            var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
            if ((is_chrome) && (is_safari)) { is_safari = false; }
    
            // If we need to narrow this down even further we can use a more robust browser detection (https://stackoverflow.com/questions/5916900)
            // Problematic browsers can be adjusted here.
            if (is_safari && inIframe() && (
                    navigator.userAgent.match(/iPad/i) ||
                    navigator.userAgent.match(/iPhone/i)
                    ))
                return true;
            else
                return false;
    
        } catch (e) {
            alert(e.message);
        }
    }
    
    // (https://stackoverflow.com/questions/326069/)
    function inIframe() {
        try {
            return window.self !== window.top;
        } catch (e) {
            return true;
        }
    }
    

    Parent Site

    Our parent site holds the IFrame that was automatically sized by Safari Mobile. Therefore the parent site now has scroll bars of it's own, and not the IFrame. We set our listener up inside the parent site to scroll itself when it receives a message from the IFramed site.

    // Safari Mobile Iframe Cross Domain Scroll Fix.
    window.onload = function () {
         // Calback function to process our postMessages.
         function receiveMessage(e) {
              try {
                   // Set the scroll position from our postMessage data.
                   // Non-Test pages should uncomment the line below.
                   //if (e.origin.includes("your-iframe-domain.com"))
                   window.scrollTo(0, e.data);
               }
               catch (err) {
               }
          }
    
          // Setup and event to receives messages form our iframe (or window)
          window.addEventListener('message', receiveMessage);
    }
    

    Hopefully this helps someone else dissect Safari Iframe problems on mobile. Also, let me know if I've overlooked a better solution.

提交回复
热议问题