Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use window.location.replace in an iframe?

We can use window.location.replace to avoid history, and to target on-page anchors without page reloads, but *not in iframes?

The problem is a CSP (content security policy) violation, which states script-src 'unsafe-inline' must be enabled. Except I don't have a CSP defined, and even if I define one and allow script-src 'unsafe-inline' it still gives the same violation error. Same result in ie11/chrome/ff.

iframe on the same domain (in the same directory).

  1. Target the iframe in the console and use window.location.replace('/samepage.html#onpage_anchor') in console.
  2. It works. It targets the on page anchor without reloading the page, and without history.
  3. Put the same code inline on anchor links and it works.
  4. Use the same code in external script, get the csp violation error. This works fine if not in an iframe.

I tried creating a CSP to allow the action, but not even the most permissive content security policies possible would allow it.

So I put together examples on plunker which allows multiple files so I could use proper hrefs which reference the parent/child pages.

Notes about the plunker examples:

  1. The problem is not reproduced in these examples. The script works perfectly, even in the iframe. However, the same code does not work on my local server, or when I run it live on a VPS.
  2. I suspect the CSP violation doesn't get triggered on plunker because plunker is presenting content to the browser via a kind of abstraction layer of some sort.
  3. The first time you click the accordion links in the parent, it causes a refresh. This is because the way the page initially loads it doesn't reference index.html. Subsequent clicks work as expected without page reloads. Not an issue in the iframe because it does initially reference child.html
  4. These are good examples to show the code without requiring alterations to make it work (as in the need to change the hrefs to make them work in stackoverflow snippets, mentioned below). It is also good as it shows the javascript working as it should. But it does not show the actually problem. You will still need to load it up in your editor and run it on a local server or live hosting environment to see the real problem.

Plunker examples: With script/without history. Without script/with history


Simple accordion with one entry. Sufficient to reproduce issue.

Clicking open/close will expand/collapse accordion, no JS required. The JS should do the exact same thing but without history. Works fine, but not in an iframe.

Code snippet notes:

  1. You can run the snippet to get an idea about what I am describing, but it does not actually demonstrate the issue.

  2. The snippet does not behave the way it would in a real browser, the javascript does not work.

  3. The snippet shows the code, but it should be run in an iframe to see the issue. Run it outside an iframe to see the difference and how it should work.

  4. Because of how the links work with the JS (replacing the whole url) they actually must be like this href="/thispage.html#ac1" rather than just href="#ac1" as they appear in the snippet (can't target the actual html page in the snippet). So if you try this in your editor (please do), then remember to change the links to this format this_document.html#anchor so they are still same page anchors, but the page.html is included in the link.

$(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);

    });

This is very simple:

  1. acAnch function takes the href attribute and drops it into window.location.replace().
  2. Listen for clicks on anchors within the accordion to run the acAnch function.

So all the script does is run window.location.replace('/this_same_page.html#on_page_anchor')

If you put that in the console it works, no CSP violation. But running it from external script doesn't work.

Inline on the links works fine:

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

Putting that on the respective links works perfectly, but I really prefer not to use inline script like that. There must be a way to do this with an external script.

I tried running the javascript on parent instead of in the iframe (with modifications to select the links within the child of course). Same CSP error result.

Why am I doing this? Well the site is much more complex than the example. Anchors in iframes work fine but they add history. If you run the code above without the javascript, (or just run the snippet), open and close the accordion a few times, and use back button, it will go back through the open close states.

I wouldn't mind the history, but if it is in an iframe, when you leave the parent page and then come back to it, the history in the iframe is broken. Going back doesn't go back through the accordion states anymore, but instead just keeps reloading the iframe. Initially the anchors don't cause iframe reloads but just steps through accordion state history, which works fine, until you leave the page and come back. Then back no longer goes through the accordion states, but just goes through a pile of identical iframe reloads. It is very user unfriendly behavior.

I don't need to use location.replace if there is another method that will work. I have tried many other approaches though, and I've found that methods that can achieve the same result, generally result in the same error.

The goal is simply to activate the anchor links on page without reloading, and without history, inside an iframe.

The inline script works. Can we make it work in an external .js file?

like image 514
Veneseme Tyras Avatar asked Nov 14 '19 04:11

Veneseme Tyras


People also ask

Why is the location replace () method preferred over the location assign () method?

The replace() method of the Location interface replaces the current resource with the one at the provided URL. The difference from the assign() method is that after using replace() the current page will not be saved in session History , meaning the user won't be able to use the back button to navigate to it.

What does Window location replace do?

Window location. The replace() method replaces the current document with a new one.

What is window location href?

Window Location Href The window.location.href property returns the URL of the current page.


Video Answer


1 Answers

This may be a non-issue, but you mentioned this is an issue on you local server, and I noticed your code relys on relative links.

If you are not setup correctly, you may be serving resource via the file:// protocol or somehow using a localhost, not recognized as a valid TLD, which would result in file:// protocol as default, or invalidate CSP

In any event, try using absolute URLs and see if that resolves the issue

like image 53
rexfordkelly Avatar answered Oct 21 '22 08:10

rexfordkelly