AMP,iOS,滚动和位置固定重做—包装方法

AMP,iOS,滚动和位置固定重做—包装方法

原文:https://medium.com/hackernoon/amp-ios-scrolling-and-position-fixed-redo-the-wrapper-approach-8874f0ee7876

新解决方案

AMP 目前正在试验一种实现可滚动 iframes 的新方法。这在 AMP,iOS,滚动重做 2 中有详细描述——阴影包装方法

问题和原始解决方案

本文是对原帖 AMP,iOS,滚动和位置固定中描述的问题和解决方案的跟进。

回想一下,AMP 文档经常显示在一个可滚动的 iframe 中。该结构通常如下所示:

<html>
  <head>
    <title>I'm a Web App and I show AMP documents</title>
    <style>
      iframe {
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
      }
    </style>
  </head>
  <body>
    <iframe … width="100%" height="100%"
      **scrolling="yes"**
      src="[https://cdn.ampproject.org/c/pub1.com/doc1](https://cdn.ampproject.org/c/pub1.com/doc1)"></iframe>
  </body>
</html>

这通常在大多数浏览器中都能很好地工作。我们确实尝试了许多其他方法,包括根据内容调整 iframe 的大小和滚动主文档。但是它们都有明显的功能缺陷和性能问题。详见原帖

但是,iOS Safari 不支持可滚动的 iframes。换句话说,scrolling="yes"被简单地忽略了。参见本演示。长期存在的 iOS Safari 问题可以在bugs.webkit.org/149264找到。

我们在ViewportBindingNaturalIosEmbed _中找到了一个原始的解决方法。简而言之,我们滚动文档中实际的<body>元素。因此,即使 iframe 本身不滚动,iframe 的内容也会滚动。

生成的 AMP 文档如下所示:

<html AMP
    **style="overflow-y: auto; -webkit-overflow-scrolling: touch;"**>
  <head></head>
  <body
 **style="
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
      "**>
    <!-- document content -->
  </body>
</html>

iframe 现在滚动了!这是我们在 AMP 中使用了一年的解决方案。然而,随着时间的推移,问题越来越多。在原帖中有详细描述,简而言之:

  • <body>上应用position:absolute是意料之外的,会干扰作者风格。例如,一个副作用是我们不允许在<body>元素上定制边距。
  • 由于bugs.webkit.org/106133,车身的scrollTopscrollLeftscrollHeightscrollWidth不工作。如原始帖子中所述,这可以通过注入带有“假”测量元素的 DOM 来解决。
  • position:fixed-webkit-overflow-scrolling:touch集装箱里。参见bugs.webkit.org/154399
  • 偏移页眉和页脚需要在<body>上设置边框,这很昂贵,会减少可滚动区域,有时会破坏现有布局。隐藏标题会导致用户界面的明显跳转和滚动中断。

我们能对此进行改进吗?输入新的解决方案…

新的解决方案——包装方法

新的解决方案已编入ViewportBindingIosEmbedWrapper _中。

DOM 结构

在其核心,包装方法类似于可滚动的<body>。iframe 在 iOS Safari 中仍然不能滚动,因此我们需要滚动 iframe 的内容。由于滚动<body>有很多问题,我们可以创建一个可滚动的包装器,并把它放在<html><body>元素之间。换句话说,我们将在可滚动容器中包装<body>元素。

因此,新的 DOM 结构将如下所示:

<html AMP
    **style="overflow-y: auto; -webkit-overflow-scrolling: touch;"**>
  <head></head>
 **<i-amp-html-wrapper
      style="
        display: block;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
      "**>
    **<body style="position: relative;">**
      <!-- document content -->
    </body>
  </i-amp-html-wrapper>
</html>

虽然这毫无疑问看起来非常“奇怪”,但它确实解决了最初的问题——它使 iframes 在 iOS Safari 上可滚动。此外,这也解决了上述许多问题:

  • <body>元素没有强制的特殊要求:它通常被定位并具有默认的overflow:visible样式。AMP 允许 DOM 中任何地方的大多数 CSS 样式,因此这减少了我们对作者样式的干扰。
  • 可滚动的包装元素可用于读取scrollTopscrollLeftscrollHeightscrollWidth,因此不再需要原始帖子中描述的“假”测量元素。
  • 偏移页眉和页脚不再需要在<body>上设置边框——在包装元素上简单填充就足够了。

然而,position:fixed问题仍然存在于该解决方案中。稍后将详细介绍。

两个元素

我们实现了包装器解决方案,但很快遇到了一个小问题。很多人喜欢使用我们通过在<html><body>之间注入i-amp-html-wrapper而打破的html > body选择器。作为修正,我们将i-amp-html-wrapper重命名为<html>

最终的 DOM 结构如下所示:

<html AMP
    style="overflow-y: auto; -webkit-overflow-scrolling: touch;">
  <head></head>
 **<html id="i-amp-html-wrapper"**      style="
        display: block;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
      ">
    <body style="position: relative;">
      <!-- document content -->
    </body>
  </html>
</html>

双倍的“奇怪”,双倍的乐趣。但是 CSS 选择器现在可以正常工作了。

履行

AMP 运行时在启动时尽早创建包装器。现有的<body>元素简单地在包装器中重新处理:

// Create wrapper.
const wrapper = document.createElement(**'html'**);
wrapper.id = **'i-amp-html-wrapper'**;// Setup classes and styles.
wrapper.className = document.documentElement.className;
document.documentElement.className = '';
document.documentElement.style = '...';
wrapper.style = '...';// Attach wrapper straight inside the document root.
**document.documentElement.appendChild(wrapper);**// Reparent the body.
const body = document.body;
**wrapper.appendChild(body);
Object.defineProperty(document, 'body', {
  get: () => body,
});**

这段代码非常简单,有一点细微差别——重定义主体会将document.body重置为null,因此我们需要将document.body属性重写回原始的<body>元素,这是使用Object.defineProperty完成的。

位置:已修复问题

虽然包装器方法解决了许多问题,但是position:fixed问题仍然存在。

这个问题在原帖中有详细描述。相关的 iOS Safari bug 是bugs.webkit.org/154399

简而言之,-webkit-overflow-scrolling:touch容器内的一个position:fixed元素在滚动时会跳跃和闪烁。看起来像是position:fixed元素被轻微滚动了一下,然后很快又跳回了正确的位置。参见此视频进行演示

在我们最初的解决方案中,我们将合格的position:fixed元素从最初的<body>中移出,放入位于-webkit-overflow-scrolling:touch容器外的 DOM 中的合成“固定层”元素中。

最终的 DOM 结构如下所示:

<html AMP
    style="overflow-y: auto; -webkit-overflow-scrolling: touch;">
  <head></head>
  <html id="i-amp-html-wrapper"
      style="
        display: block;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
      ">
    <body style="position: relative;">
      <!-- document content -->
    </body>
  </html> **<body id="i-amp-fixed-layer"
      style="
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        pointer-events: none;
      "**>
    <!-- fixed elements reparented here -->
  **</body>**
</html>

因此,我们最终得到两个<html>元素和两个<body>元素。这看起来完全疯狂,但它确实解决了两个原始问题:iframes 现在滚动和position:fixed元素不闪烁。

显然,如果 iOS Safari 的潜在问题得到解决,我们会好得多(bugs.webkit.org/149264)。

参考

黑客中午是黑客如何开始他们的下午。我们是这个家庭的一员。我们现在接受投稿并乐意讨论广告&赞助机会。

要了解更多信息,请阅读我们的“关于”页面 , 喜欢/在脸书给我们发消息,或者简单地,发推文/DM @HackerNoon。

如果你喜欢这个故事,我们推荐你阅读我们的最新科技故事趋势科技故事。直到下一次,不要把世界的现实想当然!


本站为非盈利网站,作品由网友提供上传,如无意中有侵犯您的版权,请联系删除