Detect DOMContentLoaded in iframe
Asked Answered
J

4

25

I was surprised to find the following doesn't appear to work, insofar as the DOMContentLoaded event doesn't fire (this.els is an object of elements).

this.els.stage_ifr.prop('src', 'templates/'+ee.globals.creating+'/item'+this.id);
this.els.stage_ifr[0].addEventListener('DOMContentLoaded', function() {
    alert('loaded!');
}, false);

The page loads into the iframe fine, but no callback. The DOM level zero onload, however, works.

this.els.stage_ifr[0].onload = function() { alert('loaded!'); }; //<-- fires

A workaround is to prepare a globally-accessible jQuery deferred object in the parent page and resolve it via a DOM-ready event fired from the page called into the iframe, rather than listening for DOM-ready from the parent.

Paraent page:

dfd = new $.Deferred;
dfd.done(function() { alert("frame page's DOM is ready!"); });

Frame page:

$(function() { window.parent.dfd.resolve(); });

Nonetheless it would be good to know what's up with the first approach...

Jaquesdalcroze answered 6/6, 2013 at 11:20 Comment(5)
Exceptionally I provide a link to MSDN: iframe element. As you can read at that page, DOMContentLoaded can't be attached to an iframe.Djokjakarta
Interesting - I should have thought this was perfectly fine provided the frame is of the same domain. If this is indeed case closed, feel free to post it as the answer and I'll accept.Jaquesdalcroze
You can try to attach DOMContentLoaded (a method of document only) to stage_ifr[0].contentDocument, I'm just not sure, if it would exist at the time you'll need it...Djokjakarta
I'll give it a go. The jQuery deferred workaround works in case it's a no-go. Thanks.Jaquesdalcroze
See https://mcmap.net/q/136814/-how-to-detect-when-an-iframe-has-already-been-loadedBloodandthunder
R
38

In the process of answering this question, I discovered the reason your DOMContentLoaded event listener doesn't work. It appears to me that you have two issues.

First, you're trying to listen for the DOMContentLoaded event on the iFrame itself. That isn't an iFrame event. It is a document event. So you have to reach into the iFrame to get the contentWindow and then get the document from that. That leads into the second issue.

Second, when an iFrame is first created, it has a dummy document in it that is NOT the same document as will eventually be there when dynamic content is loaded via the .src attribute. So, even if you did:

this.els.stage_ifr.contentWindow.document

to get the document in the iFrame, it will not necessarily be the right document and thus the DOMContentLoaded event won't fire on it (I've seen this behavior in Chrome).


MDN says that one can listen for the DOMFrameContentLoaded event on the iFrame itself and this will correspond with when the underlying document actually gets DOMContentLoaded. Unfortunately, I can't get this event to work in any browser. So, at this moment, the only work-around I know of is to either trigger the load event from within the iFrame itself where it can listen to its own DOMContentLoaded event (it can call out to the parent window if need be) or to just listen for the load event on the iFrame object and know that it won't fire until resources such as style sheets and images in the iFrame are also loaded.


Anyway, I thought I'd explain some of what was going on with your initial code and offer another solution even though this question was posted more than a year ago (though never answered).


Update:

I've developed a method of tracking DOMContentLoaded for an iFrame loaded with the same origin as its parent. You can see the code here.

Raji answered 8/7, 2014 at 0:25 Comment(2)
I was already asking myself why readyState is always completed in Chrome. Now I know it's because Chrome sets an empty document and creates the actual iframe document (related to the src attribute) after that. Such a bad behavior, if Chrome would handle this like other browser we could simply use readyState.Bloodandthunder
DOMFrameContentLoaded is not only a Mozilla-specific event but also an "add-on-specific event", which is "never exposed to web content and can only be used in chrome content context".Parsec
E
11

After trying different options I found that the following code works for me:

var iframe = document.getElementById("app-frame-id");
iframe.contentWindow.addEventListener("DOMContentLoaded", onFrameDOMContentLoaded, true);
function onFrameDOMContentLoaded () { 
     console.log("DOMContentLoaded");
};
Enclosure answered 7/8, 2015 at 6:37 Comment(3)
This doesn't work when you change the iframe's sourceKossuth
Seems to work for me. But could you please describe what this exactly does and why you aren't assigning this event to the document property of contentWindow?Bloodandthunder
@Bloodandthunder copying from other answer "Second, when an iFrame is first created, it has a dummy document in it that is NOT the same document as will eventually be there when dynamic content is loaded via the .src attribute"Unanimity
R
2

If your page and the iframe are on the same domain you have to wait for the original page to fire DOMContentLoaded first, then attach a DOMContentLoaded event listener on the iframe's Window (not Document).

Given you have an iframe as follows,

<iframe id="iframe-id" name="iframe-name" src="..."></iframe>

the next snippet will allow you to hook into the iframe's DOMContentLoaded event:

document.addEventListener('DOMContentLoaded', function () {
    var iframeWindow = frames['iframe-name'];
    // var iframeWindow = document.querySelector('#iframe-id').contentWindow
    // var iframeWindow = document.getElementById('iframe-id').contentWindow

    iframeWindow.addEventListener('DOMContentLoaded', function () {
        console.log('iframe DOM is loaded!');
    });
});
Raptor answered 9/3, 2020 at 22:42 Comment(0)
U
1

This might be more of a hack, but helped me solve a similar issue.

I am listening for onmouseenter of the frame content now.

This event triggers ahead of load (if the user moves the mouse). But as in my case I need the event for initialising a context menu, using the mouse was a precondition anyway.

It even triggers when the frame content changes under the mouse.

Uncovered answered 1/12, 2017 at 10:1 Comment(1)
Interesting approach. Thanks for the heads up.Jaquesdalcroze

© 2022 - 2024 — McMap. All rights reserved.