32

How to "implement" back and forward buttons in an iframe with JS?

5 Answers5

44

Use the window.history object.

// For the current window
window.history.back();     
window.history.forward();

// For an iframe's window
iframe.contentWindow.history.back(); 
iframe.contentWindow.history.forward();

or

iframe.contentWindow.history.go(-1); // back
iframe.contentWindow.history.go(1);  // forward

https://developer.mozilla.org/en/dom/window.history

Andy E
  • 311,406
  • 78
  • 462
  • 440
  • @TheDeveloper: really? Works fine here in Chrome 29.0.1521.3 dev, and it used to work in older versions of Chrome. Perhaps they broke it and then fixed it again. – Andy E Jun 08 '13 at 15:41
  • 1
    If the iframe is sandboxed (i.e. pointing to a different server than the containing page) then this doesn't work. – podperson Oct 10 '13 at 16:12
  • 11
    The ugly problem here is that when your iframe is already at the start of its own history - and there's no way to detect this condition in script - doing another back() will effectively trigger a back() on the containing page instead, which is almost certainly not what you want. This is true in all the browsers I've tested so far; Chrome 31.0.1650.57, Firefox 24, IE7... – mrec Nov 20 '13 at 19:24
  • 1
    @MikeC: yes, that is an ugly problem! – Andy E Nov 21 '13 at 12:59
  • 2
    you could also use a listener (or an uglier solution) to get all the visited urls from the iframe, store them in an array, then manually implement a back and a forward buttons which changes the iframe SRC according to the prev/next item (if defined) in the array – Rayjax May 22 '14 at 13:37
  • 4
    talking about ugly, to prevent the back button pressed in the start of iframe's window, you can add a variable "history=0" when the iframe loads and disable back button. Forward history+1, Back history-1. – Ricardo BRGWeb Jan 13 '16 at 19:14
11

Update for 2017: There is no way whatsoever to do this if the origin of the iframe content is different from the origin of the enclosing page - unless you control the content at the remote origin and can have it accept postMessage events. If the origin is the same, the older answers still work.

If this is in a WebView within an application you control, you can also set up a hook native side to control this.

Adam Leggett
  • 2,797
  • 23
  • 21
7

Button within frame:

<input type="button" value="Back" onclick="history.back()">

Button within parent frame:

<input type="button" value="Back" onclick="frame_name.history.back()">
Matthew
  • 7,831
  • 11
  • 56
  • 80
Pavel Strakhov
  • 35,459
  • 4
  • 76
  • 117
1

Thanks everybody for your valuable pointers :-) I've managed to start from your suggestions and build on them a little further.

I have a webpage that has two vertical frames: a narrow column on the left for a menu bar (frame name="menu"), and a main window on the right (frame name="main"), spanning most of the width. I.e., the index.htm contains just a frameset across the whole surface. Now... to address the general "back" navigation problem in my simple frameset, I managed to use Pavel Strakhov's suggestion with input type=button - only I had to elaborate the DOM path a little further. As I have the button in the "menu" frame on the left, and the content to be navigated happens in the "main" frame on the right, the two frames are siblings, rather than a parent vs. child. Thus, in my case, I had to traverse one level up in the hierarchy, before I was able to refer to the sibling frame. As a result, in my case the button element reads

<input type="button" value="Back in the main frame" onclick="window.parent.main.history.back()">

or you can refer to the frame by ordinal index (zero-based) in an array, rather than by name:

<input type="button" value="Back in the main frame" onclick="window.parent.frames[1].history.back()">

I have also noticed that the whole 'onclick' property can be strapped onto pretty much any other visible element, such as an anchor/href (A), a DIV or a P. I've tried those as well, to make the "back" button look more like the rest of my menu, but ultimately went back to the crude "buttonish" look and behavior, to make the button more obvious / prominent / distinct. It looks raw but it works best.

Actually in my case, windows.parent actually refers to the top frame - so there's another way to refer to my frame called "main" and tell it to go back: onclick="top.main.history.back()" . Or, apparently, onclick="top.frames['main'].history.back()" .

And then I found out, that it still doesn't work reliably. Quirks discovered so far:

  • in Firefox around v42, if you go back and then use the browser's "forward" button, several more URL clicks down the road you can get funny results: sometimes the history.back() function in a frame will result in a history.back() on the top frame, for no apparent reason.

  • in Firefox around v42, sometimes the browser's own "back" button results in per-frame history.back(), rather than a top-frame history.back(), for no apparent reason (= inconsistent behavior of the native "back" button)

  • regardless of the browser make and version, some websites apparently clear the history of their base frame on site load. If you load such a site in a frame, the per-frame history.back() does nothing.

This is possibly another reason (apart from homogeneous styling) why hardly anyone uses the good old HTML frames anymore. The resulting behaviors are not very deterministic / foreseeable / obvious to the user. This is probably why webmasters prefer fixed columns, implemented by tables or by more modern means. Or perhaps it's just because everyone uses a CMS nowadays anyway.

frr
  • 194
  • 1
  • 5
0

As others have mentioned, this is a major issue these days due to seemingly flawed security changes. In particular, as "mrec" says in a comment prior, The ugly problem here is that when your iframe is already at the start of its own history - and there's no way to detect this condition in script - doing another back() will effectively trigger a back() on the containing page instead, which is almost certainly not what you want. -- indeed, this was an insurmountable issue for me.

This solution is a tested version of the solution mentioned by responses here, whereby history is handled manually and the user cannot traverse back before history started. It was used in http://www.0AV.com for inline help and has been simplified here which may have introduced errors to the otherwise tested version. It will also likely need some refinement for your requirement.

JS in the host page..

var Hstory = [], sorc = '';

window.onmessage = function(e){ //# message fired by iFrame with onmousedown="window.top.postMessage('...','*');" or etc.
    var hilitedCrefText = e.data;    
    switch(String(hilitedCrefText)){
        case "Help_Home": //# defined in iframe
            sorc = "/index.html"; //# eg
            HistryManager(sorc); //# store
            break;
        case "Help_Back": //# defined in iframe
            sorc = HistryManager(); //# retrieve
            break;
        default: //# anything else that is generated by a link.
            HistryManager(sorc);
    }    
    if( sorc.length > 0 ) document.getElementById('your_iframe_id').src = sorc;  
}


function HistryManager(toPush) { //# history_handler
    if (toPush != undefined) {
        Hstory.push(toPush);
    }else{
        if( Hstory.length > 1 ){
            var az = Hstory[Hstory.length-2]; //..
            //# -2 is: -1 as is base 1, + -1 as it just stored this location, so must go back to PRIOR locastion.
            Hstory = Hstory.slice(0, -1); // Reduce array (Use neg num to select from end of array)
            return az;
        }else{
            alert('End of history. \n\n(Can not go further Back).');
            return '';
        }
    }
} 

JS / HTML in the iframe page..

    <button onclick="window.top.postMessage('Help_Back','*');">Back</button>
    <button onclick="window.top.postMessage('Help_Home','*');">Home</button>

    <button onclick="window.top.postMessage('helppage1.html','*');">HelpOn1</button>
    <button onclick="window.top.postMessage('helppage2.html','*');">HelpOn2</button>

..last 2 are examples of links user can traverse into (and add to history), in this case as buttons, but easily changed to anchors.

Hope that helps someone. (Tip: I misspelled some of my vars to prevent confusion with system vars). Code is an addition to Codiad, and as such is MIT License.

www-0av-Com
  • 596
  • 8
  • 13
  • Ps: this works across different domains, but if you don't control the iframes, obviously you can't insert postMessage code. In that case, you could put the Back buttons etc in the top (host) page, but then you would have to work out how to get the changing URL from the iframe (and that probably is impossible if domains are different from the top page). Another caveat - This does not disable the forward & back entries in the browser's right-click drop down context menu. Fortunately not many would use those. – www-0av-Com Apr 07 '18 at 23:20