首页 > 解决方案 > 我可以在 iframe 中使用 window.location.replace 吗?

问题描述

我们可以window.location.replace用来避免历史记录,并在不重新加载页面的情况下定位页面锚点但*不在 iframe 中?

问题是违反 CSP(内容安全策略),script-src 'unsafe-inline'必须启用这些状态。除了我没有定义 CSP,即使我定义了一个并允许script-src 'unsafe-inline'它仍然给出相同的违规错误。ie11/chrome/ff 中的结果相同。

iframe 在同一个域(在同一个目录中)。

  1. 在控制台中定位 iframe 并在控制台中使用window.location.replace('/samepage.html#onpage_anchor')
  2. 有用。它以页面锚点为目标,无需重新加载页面,也没有历史记录。
  3. 将相同的代码内联在锚链接上,它就可以工作。
  4. 在外部脚本中使用相同的代码,得到 csp 违规错误。如果不在 iframe 中,这可以正常工作。

我尝试创建一个 CSP 来允许该操作,但即使是最宽松的内容安全策略也不会允许它。

因此,我将示例放在允许多个文件的 plunker 上,以便我可以使用引用父/子页面的正确 href。

关于 plunker 示例的注释:

  1. 这些示例中没有重现该问题。该脚本运行良好,即使在 iframe 中也是如此。但是,相同的代码在我的本地服务器上不起作用,或者当我在 VPS 上实时运行它时。
  2. 我怀疑 CSP 违规不会在 plunker 上触发,因为 plunker 正在通过某种抽象层向浏览器呈现内容。
  3. 第一次单击父项中的手风琴链接时,会导致刷新。这是因为页面最初加载它的方式没有引用 index.html。随后的点击按预期工作,无需重新加载页面。iframe 中不是问题,因为它最初确实引用了 child.html
  4. 这些是显示代码的好示例,无需更改即可使其工作(如需要更改 href 以使其在 stackoverflow 片段中工作,如下所述)。它也很好,因为它显示了 javascript 应该正常工作。但它并没有显示实际的问题。您仍然需要在编辑器中加载它并在本地服务器或实时托管环境中运行它才能看到真正的问题。

Plunker 示例:带脚本/不带历史记录没有脚本/有历史


一个条目的简单手风琴。足以重现问题。

单击打开/关闭将展开/折叠手风琴,无需 JS。JS 应该做同样的事情,但没有历史。工作正常,但不在 iframe 中。

代码片段注释:

  1. 您可以运行该代码段以了解我所描述的内容,但它实际上并没有说明问题。

  2. 该代码段的行为方式与真实浏览器中的不同,javascript 不起作用。

  3. 该片段显示了代码,但它应该在 iframe 中运行以查看问题。在 iframe 之外运行它以查看差异以及它应该如何工作。

  4. 由于链接如何与 JS 一起工作(替换整个 url),它们实际上必须是这样的,href="/thispage.html#ac1"而不仅仅是href="#ac1"它们出现在片段中(不能针对片段中的实际 html 页面)。因此,如果您在编辑器中尝试此 操作(请这样做),请记住将链接更改为这种格式 this_document.html#anchor,以便它们仍然是相同的页面锚点,page.html 包含在链接中。

$(document).ready(function() {

  // anchor links without history
  $.acAnch = function(event) {
    event.preventDefault();
    var anchLnk = $(event.target);
    var anchTrgt = anchLnk.attr('href');
    window.location.replace(anchTrgt);
  }
  // listen for anchor clicks
  $('.accordion').on('click', 'a', $.acAnch);

});
div#sample.example .accordion {
  margin-left: 50px;
  margin-top: 50px;
}

div#sample.example section {
  box-sizing: border-box;
  clear: both;
  position: relative;
  display: block;
  width: 300px;
  height: 32px;
  padding: 0;
  background-color: #fff;
  box-shadow: inset 0 0 1px 1px #000;
  overflow: hidden;
}

div#sample.example section:target {
  height: auto;
}

div#sample.example a {
  box-sizing: border-box;
  display: block;
  float: right;
  width: 50%;
  height: 32px;
  margin: 0;
  padding: 4px;
  text-align: center;
  font-size: 16px;
  color: #000;
  background-color: #fff;
  box-shadow: inset 0 0 1px 1px #000;
}

div#sample.example p {
  box-sizing: border-box;
  clear: both;
  display: block;
  width: 100%;
  padding: 16px;
  margin: 16px 0 0;
  text-align: center;
  color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="sample" class="example">
  <article class="accordion">
    <section id="ac1">
      <a href="#ac0" class="ac-close">Close</a>
      <a href="#ac1" class="ac-open">Open</a>
      <div class="ac-content">
        <p>The elephants talking in their sleep kept me up so late.</p>
      </div>
    </section>
  </article>
</div>

$(document).ready(function() {

      // anchor links without history
      $.acAnch = function(event) {
        event.preventDefault();
        var anchLnk = $(event.target);
        var anchTrgt = anchLnk.attr('href');
        window.location.replace(anchTrgt);
      }
      // listen for anchor clicks
      $('.accordion').on('click', 'a', $.acAnch);

    });

这很简单:

  1. acAnch 函数获取href属性并将其放入window.location.replace().
  2. 监听手风琴内锚点的点击以运行 acAnch 函数。

所以所有的脚本都是运行window.location.replace('/this_same_page.html#on_page_anchor')

如果你把它放在控制台中,它就可以工作,不会违反 CSP。但是从外部脚本运行它不起作用。

链接上的内联工作正常:

onclick="event.preventDefault();window.location.replace('/thispage.html#acc0');"
onclick="event.preventDefault();window.location.replace('/thispage.html#acc1');"

把它放在各自的链接上效果很好,但我真的不喜欢这样使用内联脚本。必须有一种方法可以使用外部脚本来做到这一点。

我尝试在父级而不是在 iframe 中运行 javascript(当然需要修改以选择子级中的链接)。相同的 CSP 错误结果。

我为什么要这样做?好吧,该站点比示例复杂得多。iframe 中的锚点工作正常,但它们增加了历史记录。如果您在没有 javascript 的情况下运行上面的代码,(或只运行代码段),打开和关闭手风琴几次,然后使用后退按钮,它将返回打开关闭状态。

我不介意历史记录,但如果它在 iframe 中,当您离开父页面然后返回到它时,iframe 中的历史记录就会被破坏。返回不再通过手风琴状态返回,而是继续重新加载 iframe。最初,锚不会导致 iframe 重新加载,而只是逐步浏览手风琴状态历史,这工作正常,直到您离开页面并返回。然后 back 不再经历手风琴状态,而只是经历了一堆相同的 iframe 重新加载。这是对用户非常不友好的行为。

如果有另一种可行的方法,我不需要使用 location.replace 。虽然我尝试了许多其他方法,但我发现可以达到相同结果的方法通常会导致相同的错误。

目标只是在 iframe 内激活页面上的锚链接,而无需重新加载,也无需历史记录。

内联脚本有效。我们可以让它在外部 .js 文件中工作吗?

标签: javascripthtmliframebrowser-historycontent-security-policy

解决方案


这可能不是问题,但您提到这是本地服务器上的问题,我注意到您的代码依赖于相关链接。

如果您的设置不正确,您可能正在通过 file:// 协议或以某种方式使用 localhost 来提供资源,这不会被识别为有效的 TLD,这将导致 file:// 协议成为默认协议,或者使 CSP 无效

无论如何,请尝试使用绝对 URL,看看是否能解决问题


推荐阅读