跨域IFrame element.scrollIntoView()Safari问题

时间:2019-02-09 05:26:48

标签: javascript jquery iframe safari

我遇到了一个困扰整个Google的问题,但是所提供的解决方案均无法正常工作。我认为这些解决方案中的大多数都不考虑跨域。

我有一个网站,我想指示用户使用(整页)iframe嵌入他们的网站。问题仅在某些版本的Safari上存在。 IFrame中的元素无法滚动到视图中。

enter image description here

我注意到,如果我进行相同的域测试,则IFrame可以使用window.parent.scrollTo(0,element.top)进行滚动。这可行,但不能跨域。另一个奇怪的是,没有其他浏览器需要window.parent方法来滚动IFrame,而仅是Safari。所有其他浏览器都可以在IFrame中使用element.scrollIntoView()。请注意,我已经使用JavaScript workaround通过跨协议IFrame来取悦Safari。

我仅在IFrame内部的Safari Mobile上看到的另一个问题是,向下滚动时,Bootstrap Modals在IFrame的顶部看不见。虽然,我确定如果可以正确设置滚动位置,那么我们也应该能够设置模式位置。

这就是我尝试过的;

 1. window.frames['IFrameName'].document.
    getElementById("elmId").scrollIntoView();
  1. Offset trick
  2. Velocity.js

(我认为)我最后的选择是在IFrame中使用postMessage来通知父域以设置框架的滚动位置。

在我看来,这个问题已经存在了很长时间。有没有比这更好的方法了?

2 个答案:

答案 0 :(得分:1)

这最终导致比代码更多的研究。发生的是-我有一些代码根据内容调整了IFrame的大小。

在所有其他浏览器中,它可以正常工作并消除滚动条。原来Safari automatically sizes the Iframe保留了自己的滚动功能。在我的应用程序中,静态页面为零。这给我留下了无法使用链接中描述的scrolling=no修复程序的问题。

确切地了解了所发生的情况之后,我采取了另一种方法来修复elm.scrollIntoView()。该代码是注释,然后是重要部分;

  1. 确定何时使用RequiresIframeScrollFix应用iframe修订
  2. 使用elm.getBoundingClientRect().top从iframe中获取滚动位置。
  3. 与父母交流以滚动window.parent.postMessage
  4. 通过window.addEventListener('message',...)在父级中接收邮件

这是它的样子。

iframe网站

我们的iframe网站当前将其元素滚动到这样的视图中elm.scrollIntoView();,我们将其更改为以下内容。

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.

可选:使用elm.getBoundingClientRect().top修复了IOS IFrame中的引导模式定位。

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

RequiresIframeScrollFix()主要由一些很好的文档代码组成,这些代码围绕SO来确定我们是否位于IPad或IPhone上的iframe中。

// 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;
    }
}

父网站

我们的父站点保存由Safari Mobile自动调整大小的IFrame。因此,父网站现在拥有自己的滚动条,而不是IFrame。我们在父网站内部设置了侦听器,以在其从IFramed网站收到消息时进行滚动。

// 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);
}

希望这可以帮助其他人在移动设备上解决Safari Iframe问题。另外,让我知道是否我忽略了更好的解决方案。

答案 1 :(得分:0)

另一个选项是iframeResizer库。您可以在iframePage中使用两种方法:scrollTo和scrollToOffset,它们的作用与您描述的大致相同-它们通过消息进行通信。它在Safari中为我们解决了这个问题。

在父页面内部,为iframe设置大小调整时,您必须为其onScroll事件分配一个回调函数:

iframeNode.iframeResize({ 
  ...,
  onScroll: ({x,y}) => callback
}

在iframe页面中:

if('parentIFrame' in window){
  window.parentIFrame.scrollTo(0, someNode.offsetTop);
}