我能在iframe中使用window.location.replace吗?

23
我们可以使用window.location.replace避免历史记录,并且在页面锚点上定位而不需要重新加载页面,但是在iframes中无法实现?
问题是CSP(内容安全策略)违规,它要求启用script-src 'unsafe-inline'。 除非我定义一个CSP并允许script-src 'unsafe-inline',否则即使我定义了一个CSP并允许它,它仍然会给出相同的违规错误。 在ie11 / chrome / ff中结果相同。
iframe与同一域(在同一目录中)。
  1. 在控制台中定位iframe,并在控制台中使用window.location.replace('/samepage.html#onpage_anchor')
  2. 它起作用。 它定位到页面锚点,而不需要重新加载页面并且没有历史记录。
  3. 将相同的代码内联到锚链接中,它是有效的。
  4. 将相同的代码用于外部脚本,会得到csp违规错误。 如果不在iframe中,则这很好运行。
我尝试创建CSP以允许该操作,但即使是最寛松的内容安全策略也不允许它。
因此,我在plunker上制作了示例,以便可以使用引用父/子页面的proper hrefs。
有关plunker示例的注释:
  1. 这些示例未能在本地服务器上或在VPS上运行。 代码在iframe中无法工作,但是在plunker中可以正常工作。
  2. 我怀疑CSP违规未能在plunker上触发,因为plunker通过某种抽象层向浏览器呈现内容。
  3. 第一次单击父级中的手风琴链接会导致刷新。 这是因为页面最初加载时它没有引用index.html。 后续点击将按预期工作而不需要重新加载页面。 在iframe中没有问题,因为它最初是引用child.html的
  4. 这些示例非常好,可用于在Stack Overflow片段中展示代码,无需更改href即可使其工作。 它也很好,因为它显示了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');"

将这段代码放在相应的链接上可以完美地工作,但我真的不想使用内联脚本。一定有一种方法可以通过外部脚本来实现。

我尝试在父级页面上运行javascript,而不是在iframe中(当然需要修改以选择子链接)。结果还是出现了CSP错误。

为什么要这样做呢?因为这个网站比示例要复杂得多。iframe中的锚点很好用,但它们会增加历史记录。如果您没有运行上面的javascript代码(或者只是运行了片段),打开并关闭几次手风琴,再使用后退按钮,它将返回到打开关闭状态。

我不介意历史记录,但如果它在一个iframe中,当您离开父页面然后再回到它时,iframe中的历史记录就被破坏了。返回不再通过手风琴状态历史记录,而是不断重新加载iframe。这种行为对用户非常不友好。

如果有其他可行的方法,我并不需要使用location.replace。虽然我已经尝试了许多其他方法,但发现达到相同结果的方法通常会产生相同的错误。

目标很简单,即在iframe中激活页面上的锚点链接,而无需重新加载和历史记录。

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


你是想要跳转到锚点吗?如果是,那么 <a href="#ac0" class="ac-close">Close</a> 应该可以实现。 - Rafael Herscovici
好的,我在 Plunker 上设置了示例。不幸的是,在 Plunker 上没有重现问题。相反,脚本运行良好,即使在 iframe 中也是如此。带有脚本-无历史记录没有脚本有历史记录。Plunker 上的小问题是,父级手风琴链接会导致页面刷新,只有第一次点击时(最初 Plunk 加载时没有 index.html 引用,因此在第一次点击后,它按预期工作,而无需重新加载页面)。对于 iframe 来说不是问题,因为它是通过 child.html src 加载的。 - Veneseme Tyras
至少通过plunker的示例,您可以看到完整的代码并了解它应该如何工作。但是在我的本地服务器上或在VPS上实时运行时,它无法正常工作。我将使用plunker链接和信息更新问题。 - Veneseme Tyras
1
我将您的示例代码放到我的服务器上,它可以正常工作。然后,我从您的示例https://plnkr.co/edit/V3kx7LQbTppaQ6V06uZp?p=preview创建了一个新的plunker示例,也可以正常工作,没有CSP错误结果。 - Vaibhav J
是的,它在我做的 Plunker 上运行良好。不过我很好奇它在你的服务器上如何运行,因为在你的评论之后,我进行了三次检查,但我无法在我的实时服务器或本地服务器上使其正常工作。 - Veneseme Tyras
我怀疑一个浏览器扩展可能会干扰你的结果。由于我和其他人都没有观察到任何复制现象,这一定与a)你的机器或b)你的服务器设置有关。请尝试另一台机器,尝试另一个Web服务器。 - Gerald
6个回答

1
这可能不是问题,但你提到这是在你的本地服务器上出现的问题,我注意到你的代码依赖于相对链接。如果你没有正确设置,你可能会通过file://协议提供资源,或者以某种方式使用localhost,而不被认为是有效的TLD,这将导致默认使用file://协议,或者使CSP无效。无论如何,请尝试使用绝对URL并查看是否解决了该问题。

0

0
您可以使用锚点单击事件将活动类切换到父元素,就像这样。

// Code goes here

$(window).on('load', function() {
  $('.accordion section').on('click', '.ac-open', function(e) {
    e.preventDefault();
    $(e.target).parent().addClass('active');
  });

  $('.accordion section').on('click', '.ac-close', function(e) {
    e.preventDefault();
    $(e.target).parent().removeClass('active');
  });
});
/* Styles go here */

html,
body {
  margin: 0;
  padding: 0;
}

.flt-lft {
  float: left;
  margin: 16px;
}

h4 {
  text-align: center;
  margin: 0;
  padding: 8px;
  color: white;
  background-color: green;
}


/* #sample.example .accordion {
  margin-left: 50px;
  margin-top: 50px;
} */

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

#sample.example section.active {
  height: auto;
}

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

#sample.example p {
  box-sizing: border-box;
  clear: both;
  display: block;
  width: 100%;
  padding: 16px;
  margin: 16px 0 0;
  text-align: center;
  color: #000;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <div class="container">

    <div class="flt-lft">
      <h4>parent</h4>
      <div id="sample" class="example">
        <article class="accordion">
          <section id="ac1">
            <a href="index.html#ac0" class="ac-close">Close</a>
            <a href="index.html#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>
    </div>

    <div class="flt-lft">
      <h4>iframe</h4>
      <iframe src="child.html"></iframe>
    </div>

  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="script.js"></script>
</body>

</html>


0

可以的,以下链接提供了一个实时示例

 https://codepen.io/pmk/pen/wOwoyW

HTML

<div class="">
<h3>Testing 4 methods of writing dynamic content to Iframe.</h3>
<p>#1 use <strong>document.write()</strong>, 
#2 use    <strong>URL.createObjectURL()</strong>, 
#3 use <strong>encodeURI()</strong> and #4  <strong>iframe.srcdoc</strong></p>
<p>Using the recommended method <strong>URL.createObjectURL()</strong>  leads to problems when trying to retrieve the <strong>windown.location</strong> object.      (Same does the <strong>encodeURI()</strong> method)<p/>
<p>Only reliable method if you need window.location, seems to be the old      obsolete <strong>document.write()</strong> method.</p>
 <iframe id="iframe1"></iframe>
 <iframe id="iframe2"></iframe>
 <iframe id="iframe3"></iframe>
 <iframe id="iframe4"></iframe>
 </div>

CSS

@import url(https://fonts.googleapis.com /css?family=Fira+Sans:400,500italic);

html {
height: 100%;
background-color: rgba(34,32,36,1);
}
body {
text-align: center;
font: normal 100% 'Fira Sans', sans-serif;
color: #aaa;
}
iframe {
width: 40%;
height: 200px;
background: white;

}

JS

var template = [
'<!DOCTYPE HTML>',
'<html>',
'<head>',
'</head>',
'<body>',
'<script>',
'document.write("<pre>" + JSON.stringify(window.location, null, 4) + 

"</pre>");',
'<\/script>',
'</body>',
'</html>'
].join('');

var iframe1El = document.querySelector('#iframe1');
var iframe1 = iframe1El.contentWindow || ( 
iframe1El.contentDocument.document || iframe1El.contentDocument);

 var iframe2El = document.querySelector('#iframe2');  
 var iframe2 = iframe2El.contentWindow || 
 (  iframe2El.contentDocument.document || iframe2El.contentDocument);

 var iframe3El = document.querySelector('#iframe3');
 var iframe3 = iframe3El.contentWindow || 
 (    iframe3El.contentDocument.document ||     

 iframe3El.contentDocument);

 var iframe4El = document.querySelector('#iframe4');
 var iframe4 = iframe4El.contentWindow || 
 ( iframe4El.contentDocument.document || iframe4El.contentDocument);

 iframe1.document.open();
 iframe1.document.write(template);
 iframe1.document.close();

 var bData = new Blob([template], {type: 'text/html'});
 iframe2El.onload = function() { window.URL.revokeObjectURL(bData); };
 iframe2El.src = window.URL.createObjectURL(bData);

 iframe3El.src = 'data:text/html;charset=utf-8,' +  
 encodeURI(template);

 iframe4El.srcdoc = template;

-1

是的,您可以使用iframe。


-2

你可以使用CSS代替HTML的iframe标签,因为iframe标签在HTML中已被移除。


再次查看[答案]。他们将如何使用CSS解决这个问题? - camille

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接