204

Assuming I have no control over the content in the iframe, is there any way that I can detect a src change in it via the parent page? Some sort of onload maybe?

My last resort is to do a 1 second interval test if the iframe src is the same as it was before, but doing this hacky solution would suck.

I'm using the jQuery library if it helps.

Matrym
  • 15,021
  • 33
  • 90
  • 137
  • 3
    Will the `src` property change when a link in the iframe is clicked? I'm not sure about that - if I had to guess, if would say "no". There *are* ways to monitor properties (in Firefox at least AFAIK) but I'm not sure whether it will be of any use in this case. – Pekka Mar 11 '10 at 22:10
  • I'm trying to implement something like this, and can confirm that the `src` attribute in the DOM does *not* change in the latest versions of either FireFox or Safari. @Michiel's answer below is as good as I have been able to get so far. – Kaelin Colclasure Sep 15 '14 at 13:09

7 Answers7

221

You may want to use the onLoad event, as in the following example:

<iframe src="http://www.google.com/" onLoad="alert('Test');"></iframe>

The alert will pop-up whenever the location within the iframe changes. It works in all modern browsers, but may not work in some very older browsers like IE5 and early Opera. (Source)

If the iframe is showing a page within the same domain of the parent, you would be able to access the location with contentWindow.location, as in the following example:

<iframe src="/test.html" onLoad="alert(this.contentWindow.location);"></iframe>
jondavidjohn
  • 59,273
  • 21
  • 110
  • 154
Daniel Vassallo
  • 312,534
  • 70
  • 486
  • 432
  • 13
    You mean this.contentWindow.location.href – Nikolai Sep 27 '11 at 13:50
  • @Daniel Vassallo assuming u take this aproach when you wish to handle security issue, cant someone just override the onLoad event and then change the iframe content? – Noam Shaish Apr 19 '12 at 14:41
  • 16
    When I do `this.contentWindow.location.href` I get `Permission denied to access property 'href'` – Mazatec Apr 20 '12 at 10:13
  • Hey @Daniel , I am stuck up with a old project and this isn't workin in VS2005 + C# 2.0 – Sakthivel Mar 06 '13 at 13:25
  • I would suggest separating the inline `onLoad` event from the DOM. – Mike Kormendy Mar 17 '13 at 00:53
  • 5
    this.contentWindow.location.href wont work bcoz you are doing a cross origin request. :( All browsers restrict that now. – Naveen Sep 16 '14 at 07:58
  • And if you're like me and prefer not using an inline `onload` you can use `addEventListener` with the `load` event in your javascript. I.e. `myIframe.addEventListener('load', function () { console.log(this.contentWindow.location); });`. Note: In IE you might have to sub out `attachEvent` for `addEventListener` depending on the version. – KhalilRavanna Oct 14 '14 at 18:15
  • 4
    Correction: `this.contentWindow.location` is available for other domains. Even `this.contentWindow.location.href` is available for other domains, but only for writing. However reading `this.contentWindow.location.href` is limited to the same domain. – wadim Mar 04 '15 at 11:17
  • does it work if the iframe url is updated with "replaceState" and does not load anything? – Sebastien Lorber Jan 02 '19 at 10:40
  • this is **undefined** Doesnt working solution anymore. – Hadnazzar Mar 20 '20 at 15:44
59

Answer based on JQuery < 3

$('#iframeid').load(function(){
    alert('frame has (re)loaded');
});

As mentioned by subharb, as from JQuery 3.0 this needs to be changed to:

$('#iframe').on('load', function() {
    alert('frame has (re)loaded ');
});

https://jquery.com/upgrade-guide/3.0/#breaking-change-load-unload-and-error-removed

Michiel
  • 3,782
  • 3
  • 28
  • 40
  • 3
    Doesn't work exactly as planned. It fires the alert on the initial page load. Depending on what your plans are for this code (for me, I'm animating the dialog off the page on form submit) this solution will not work. – stacigh Mar 30 '15 at 23:17
  • @stracigh, that is correct, the event will fire every time the frame loads, so also the first time on the initial page load. If you don't want your code to be triggered the first time you must somehow detect this is your first frame load and return. – Michiel Mar 31 '15 at 11:07
  • @FooBar, Yes it does work cross domain, in fact that is what i used it for. But you cannot acces the page in the iFrame with javascript when that page is on another domain. – Michiel Apr 03 '15 at 18:36
  • this works and detect when the content is load, i need a way to detect when the src in changing. Something like beforeLoad(to activate a loader) – Andrea_86 Feb 20 '18 at 16:55
  • As indicated by @stacigh, it will fire the event once on the parent page load. If you declare a counter outside of that function and read it inside, you can then detect the changes after that. You'd increment counter inside onload handler plus have `if (counter > 1) { // do something on iframe actual change }` (assuming counter was set to 0 at start) – Alex Oct 18 '19 at 11:41
40

If you have no control over the page and wish to watch for some kind of change then the modern method is to use MutationObserver

An example of its use, watching for the src attribute to change of an iframe

new MutationObserver(function(mutations) {
  mutations.some(function(mutation) {
    if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
      console.log(mutation);
      console.log('Old src: ', mutation.oldValue);
      console.log('New src: ', mutation.target.src);
      return true;
    }

    return false;
  });
}).observe(document.body, {
  attributes: true,
  attributeFilter: ['src'],
  attributeOldValue: true,
  characterData: false,
  characterDataOldValue: false,
  childList: false,
  subtree: true
});

setTimeout(function() {
  document.getElementsByTagName('iframe')[0].src = 'http://jsfiddle.net/';
}, 3000);
<iframe src="http://www.google.com"></iframe>

Output after 3 seconds

MutationRecord {oldValue: "http://www.google.com", attributeNamespace: null, attributeName: "src", nextSibling: null, previousSibling: null…}
Old src:  http://www.google.com
New src:  http://jsfiddle.net/ 

On jsFiddle

Posted answer here as original question was closed as a duplicate of this one.

Community
  • 1
  • 1
Xotic750
  • 20,394
  • 8
  • 50
  • 71
16

Note: The snippet would only work if the iframe is with the same origin.

Other answers proposed the load event, but it fires after the new page in the iframe is loaded. You might need to be notified immediately after the URL changes, not after the new page is loaded.

Here's a plain JavaScript solution:

function iframeURLChange(iframe, callback) {
    var unloadHandler = function () {
        // Timeout needed because the URL changes immediately after
        // the `unload` event is dispatched.
        setTimeout(function () {
            callback(iframe.contentWindow.location.href);
        }, 0);
    };

    function attachUnload() {
        // Remove the unloadHandler in case it was already attached.
        // Otherwise, the change will be dispatched twice.
        iframe.contentWindow.removeEventListener("unload", unloadHandler);
        iframe.contentWindow.addEventListener("unload", unloadHandler);
    }

    iframe.addEventListener("load", attachUnload);
    attachUnload();
}

iframeURLChange(document.getElementById("mainframe"), function (newURL) {
    console.log("URL changed:", newURL);
});
<iframe id="mainframe" src=""></iframe>

This will successfully track the src attribute changes, as well as any URL changes made from within the iframe itself.

Tested in all modern browsers.

I made a gist with this code as well. You can check my other answer too. It goes a bit in-depth into how this works.

Hadnazzar
  • 926
  • 12
  • 15
dodov
  • 4,065
  • 3
  • 25
  • 44
7

The iframe always keeps the parent page, you should use this to detect in which page you are in the iframe:

Html code:

<iframe id="iframe" frameborder="0" scrolling="no" onload="resizeIframe(this)" width="100%" src="www.google.com"></iframe>

Js:

    function resizeIframe(obj) {
        alert(obj.contentWindow.location.pathname);
    }
Tarik FAMIL
  • 439
  • 5
  • 8
4

Since version 3.0 of Jquery you might get an error

TypeError: url.indexOf is not a function

Which can be easily fix by doing

$('#iframe').on('load', function() {
    alert('frame has (re)loaded ');
});
subharb
  • 3,032
  • 8
  • 34
  • 66
2

Here is the method which is used in Commerce SagePay and in Commerce Paypoint Drupal modules which basically compares document.location.href with the old value by first loading its own iframe, then external one.

So basically the idea is to load the blank page as a placeholder with its own JS code and hidden form. Then parent JS code will submit that hidden form where its #action points to the external iframe. Once the redirect/submit happens, the JS code which still running on that page can track your document.location.href value changes.

Here is example JS used in iframe:

;(function($) {
  Drupal.behaviors.commercePayPointIFrame = {
    attach: function (context, settings) {
      if (top.location != location) {
        $('html').hide();
        top.location.href = document.location.href;
      }
    }
  }
})(jQuery);

And here is JS used in parent page:

;(function($) {
  /**
   * Automatically submit the hidden form that points to the iframe.
   */
  Drupal.behaviors.commercePayPoint = {
    attach: function (context, settings) {
      $('div.payment-redirect-form form', context).submit();
      $('div.payment-redirect-form #edit-submit', context).hide();
      $('div.payment-redirect-form .checkout-help', context).hide();
    }
  }
})(jQuery);

Then in temporary blank landing page you need to include the form which will redirect to the external page.

kenorb
  • 118,428
  • 63
  • 588
  • 624
  • This may be a hack but the overhead is considerably less than mutation observer. When used in a single page application be mindful not to create any references that will prevent the leftovers from this hack from being garbage collected. – Jeff Apr 01 '16 at 19:15