iOS 12中的引导模式Iframe隐藏在内容后面

Posted

技术标签:

【中文标题】iOS 12中的引导模式Iframe隐藏在内容后面【英文标题】:Bootstrap modal Iframe in iOS 12 hidden behind content 【发布时间】:2019-12-22 02:37:59 【问题描述】:

我在 ios 12 上的 Safari 的 iframe 中显示的 Bootstrap 4.1 模式存在问题。测试的所有其他浏览器都按预期工作(甚至是 iOS 11 上的 Safari)。这个问题似乎是 iOS 12 特有的。

我创建了一个minimal example demonstrating the issue。前两个按钮似乎按预期工作,但是最后 4 个按钮您可以看到问题,当您向下遍历时,每个按钮都变得更糟,当您尝试滚动或关注内部元素时,最后一个按钮一起消失模态(见下面的截图):

我会注意到,我们处理此功能的方式可能有点不正统,因为我们不是允许 iframe 的内容滚动,而是在加载后通过在父子节点之间传递消息来调整它的高度message 事件处理程序和 postMessage: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage 这就是我怀疑有问题的地方(但尚未能够追踪到它(如前所述,这只是运行版本 12 的 ios 设备上的问题)。

编辑

最近发现这个问题不是 iOS 12 上的 Safari 特有的,chrome 也是如此。

以下代码来自之前的最小示例链接:

父级 (/modal-test/index.html):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Title</title>

    <link rel="stylesheet" href="./bootstrap.min.css">

    <script src="./jquery.min.js"></script>
    <script src="./popper.min.js"></script>
    <script src="./bootstrap.min.js"></script>

    <script>
        $(document).ready(function()

            $ifCon = $("#ifCon");

            window.addEventListener("message", function(event)
                if(event.data.method === "returnWindowSize")
                    $ifCon.height(event.data.content);
                
            , false);

        );
    </script>

    <style>

        #ifCon 
            display: flex;
            width: 100%;
            height: 100%;
            flex-direction: column;
            background-color: #F2F2F2;
            overflow: hidden;
            border-radius:10px;
            border:1px solid grey;
            margin-left:auto;
            margin-right:auto;
            /*box-shadow:2px 2px 3px #000;*/
            box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.75);
        
        #ifCon iframe 
            flex-grow: 1;
            border: none;
            margin: 0;
            padding: 0;
        

    </style>


</head>
<body>

    <div class="container">

        <div class="row">

            <div class="col text-center">

                <div id="ifCon">
                    <iframe   scrolling="no" src="/modal-test/frameable.html"></iframe>
                </div>

            </div>

        </div>

    </div>

</body>
</html>

子 (/modal-test/frameable.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Title</title>

    <link rel="stylesheet" href="./bootstrap.min.css">

    <script src="./jquery.min.js"></script>
    <script src="./popper.min.js"></script>
    <script src="./bootstrap.min.js"></script>

    <script>

        var $embedContent, modalState;


        $(document).ready(function()

            $embedContent = $('#embedContent');

            parent.postMessage(method:"returnWindowSize", content:$embedContent.height(), '*');


            $('.modal').on('shown.bs.modal', function(e)

                modalState = 
                    id:$(this).attr('id'),
                    contentElement:$(this).find('.modal-content'),
                    initialContentContainerHeight:$embedContent.height(),
                    invokerElement:$(e.relatedTarget)
                ;

                adjustModal();

            );

            $('.modal').on('hidden.bs.modal', function(e)
                modalState = null;
                $(this).find('.modal-content').css("margin-top", "0px");
                $embedContent.css('height', 'auto');
                parent.postMessage(method:"returnWindowSize", content:$embedContent.height(), '*');
            );


        );


        function adjustModal()

            if(modalState.contentElement.css('margin-top') !== modalState.invokerElement.offset().top)
                modalState.contentElement.animate('margin-top':modalState.invokerElement.offset().top, 200, "linear");
            

            if(

                // modal position + modal height is greater than or equal to the height of the embedContent so we need to resize the
                // embedContent (make it taller)
                ((modalState.invokerElement.offset().top + modalState.contentElement.height()) >= $embedContent.height()) ||

                // modal position + modal height is less than or equal to the height of the embedContent AND the current height of the
                // embedContent is greater than or equal to the size of the embedContent height when the modal was originally shown
                (((modalState.invokerElement.offset().top + modalState.contentElement.height()) <= $embedContent.height()) &&
                    ($embedContent.height() > modalState.initialContentContainerHeight))

            )
                var newEmbedContentHeight = modalState.invokerElement.offset().top + modalState.contentElement.height() + 30;
                $embedContent.height(newEmbedContentHeight);
                parent.postMessage(method:"returnWindowSize", content:newEmbedContentHeight, '*');
            

        

    </script>


</head>
<body>


    <div id="embedContent" class="container">

        <div class="row" style="height:200px;margin-top:100px;">
            <div class="col text-center">
                <button id="btn1" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
                  Launch demo modal
                </button>
            </div>
        </div>

        <div class="row" style="height:200px;">
            <div class="col text-center">
                <button id="btn2" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
                  Launch demo modal
                </button>
            </div>
        </div>

        <div class="row" style="height:200px;">
            <div class="col text-center">
                <button id="btn3" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
                  Launch demo modal
                </button>
            </div>
        </div>

        <div class="row" style="height:200px;">
            <div class="col text-center">
                <button id="btn3" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
                  Launch demo modal
                </button>
            </div>
        </div>

        <div class="row" style="height:200px;">
            <div class="col text-center">
                <button id="btn3" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
                  Launch demo modal
                </button>
            </div>
        </div>

        <div class="row" style="height:200px;">
            <div class="col text-center">
                <button id="btn3" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
                  Launch demo modal
                </button>
            </div>
        </div>

    </div>


    <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" 
        aria-labelledby="exampleModalLabel" aria-hidden="true" data-focus="false" style="overflow-y:hidden;">
      <div class="modal-dialog" role="document">
        <div class="modal-content" style="margin-top:500px;">
          <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body">
            <form>
              <div class="form-group">
                <label for="exampleFormControlInput1">Email address</label>
                <input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name@example.com">
              </div>
              <div class="form-group">
                <label for="exampleFormControlInput1">Email address</label>
                <input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name@example.com">
              </div>
              <div class="form-group">
                <label for="exampleFormControlSelect1">Example select</label>
                <select class="form-control" id="exampleFormControlSelect1">
                  <option>1</option>
                  <option>2</option>
                  <option>3</option>
                  <option>4</option>
                  <option>5</option>
                </select>
              </div>
              <div class="form-group">
                <label for="exampleFormControlSelect1">Example select</label>
                <select class="form-control" id="exampleFormControlSelect1">
                  <option>1</option>
                  <option>2</option>
                  <option>3</option>
                  <option>4</option>
                  <option>5</option>
                </select>
              </div>
              <div class="form-group">
                <label for="exampleFormControlSelect1">Example select</label>
                <select class="form-control" id="exampleFormControlSelect1">
                  <option>1</option>
                  <option>2</option>
                  <option>3</option>
                  <option>4</option>
                  <option>5</option>
                </select>
              </div>
              <div class="form-group">
                <label for="exampleFormControlSelect1">Example select</label>
                <select class="form-control" id="exampleFormControlSelect1">
                  <option>1</option>
                  <option>2</option>
                  <option>3</option>
                  <option>4</option>
                  <option>5</option>
                </select>
              </div>
            </form>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div>
      </div>
    </div>




</body>
</html>

【问题讨论】:

为什么overflow-y设置为隐藏? @Roya overflow-y 被隐藏,因为不应该有滚动条。如果更改子页面的高度,则会调整父级中 iframe 容器(以及 iframe)的高度。此代码用于嵌入小部件,它需要看起来像是第三方网站的一部分,而不是其中的框架。 【参考方案1】:

解决方案最终是我们需要通过在子文档的&lt;head&gt;&lt;/head&gt;(iframe 中的元素(在我们的例子中为 frameable.html))中包含以下 css 来强制硬件加速:

<style>
    body
        transform: translate3d(0,0,0);
    
</style>

但是,我无法解释为什么这可以解决问题,或者为什么 ios12 需要它而不是 ios11。如果其他人可以对这个主题有所了解,我相信它会帮助其他人。

【讨论】:

这解决了问题,因为transform creates a new stacking context.【参考方案2】:

您需要将model-contentmargin-top 设置为父文档的滚动高度。

这会将文档完全放在顶部,您可能需要提供一些偏移值,例如+80,以使其不会粘在顶部。

'margin-top':modalState.invokerElement.offset().top

'margin-top':parent.document.documentElement.scrollTop

【讨论】:

model-content 必须与调用它的元素对齐。由于此模态在 iframe 中,如果我们将其 margin-top 设置为父 scrollTop 位置,任何固定或绝对标题将始终隐藏模态的顶部(这是用于第三方网站的可嵌入小部件,所以我们不会无法调整)。 当你说align with the element that invoked it.时,模态应该出现在哪里?比如,如果它是你拥有的最后一个按钮。 如果最后一个按钮被点击,那么modal-content的顶部与最后一个按钮的顶部对齐,调整#ifcon的高度使模态不被切断。这可以在未运行 ios12 的设备上的实时示例链接中看到。【参考方案3】:

在#ifCon 的 CSS 中添加

#ifCon 
  height: 100vh;
 

随意乱用您喜欢的数字,VH 将 View Height 设置为 100%。

【讨论】:

以上是关于iOS 12中的引导模式Iframe隐藏在内容后面的主要内容,如果未能解决你的问题,请参考以下文章

Telerik RadDatePicker 隐藏在 IE11 中的 iframe 后面

引导模式打开时如何防止滚动正文内容

键盘使用 iFrame/object 和 JQTouch 隐藏 PhoneGap Build 3.1 中的 iOS 输入字段

如何在 iframe 中打开 ajax 成功的引导模式

如果在 iframe 中,引导模式弹出窗口会忽略滚动位置

ios上网页iframe里,把页面下拉,进行元素的隐藏显示和增删节点的dom操作页面会瞬间回到顶部