contentDocument for an iframe
w3.org
contentDocument of type Document, readonly, introduced in DOM Level 2
The document this frame contains, if there is any and it is available, or null otherwise.
MDN
From the DOM iframe element, scripts can get access to the window object of the included HTML page via the contentWindow property. The contentDocument property refers to the document element inside the iframe (this is equivalent to contentWindow.document), but is not supported by Internet Explorer versions before IE8.
msdn
the document this page or frame contains
This property is new in Windows Internet Explorer 8
So to get the innerHTML of the body element you could use
iframe.contentDocument.getElementsByTagName("body")[0]
or
iframe.contentDocument.body
in todays browsers.
Iframe body when accessed through iframeElement.contentDocument.body is an empty node
You have to wait until iframe loaded completely to access it's body.
var iframe = document.getElementById('frame');
iframe.onload = function () {
console.log(iframe.contentDocument.body);
}
Difference between contentDocument and contentWindow javascript iframe/frame access properties
I think the <iframe>
MDN documentation explains it well:
From the DOM
iframe
element, scripts can get access to thewindow
object of the included HTML page via thecontentWindow
property. ThecontentDocument
property refers to thedocument
element inside theiframe
(this is equivalent tocontentWindow.document
), but is not supported by Internet Explorer versions before IE8.
So you would use contentWindow
the same way you use window
and contentDocument
as you use document
.
Accessing document inside iframe using JS. contentWindow.document and contentDocument doesn't work
The iframe on a different origin, hence fiddling with the <iframe>
element on the parent page won't permit you to view anything inside the child.
While it would be possible to set up a userscript to run on both the parent page and the child page, and then communicate between the iframe and its parent window with postMessage
, you probably only need to run the script inside the iframe. Change your metadata block to include the child page instead (mp4upload.com
), and then you can remove the ads from there.
As a side note, polling with setInterval
to hide elements is a bit inelegant - if you want to hide certain elements, you might be able to just insert a CSS stylesheet instead. For example, the ad-removal script could be something like the following:
document.body.appendChild(document.createElement('style')).textContent = `
.ad-container {
display: none;
}
`;
where you replace .ad-container
with the selector(s) of the ad containers, of course. Then there won't be need for any other Javascript, the stylesheet can take care of it all.
How to handle iframe contentDocument with Prototype
If you want to use Prototype in different documents — and iframes are different documents — you must include the script in every document, and use the right copy to access the associated document.
The same goes for jQuery and any other framework that refers directly to document
. Each instance of the library is associated with its own document
object. So when you create an element from the parent script, its ownerDocument
is the parent window. Try to append that element inside the iframe's document and you should get a DOMException.WRONG_DOCUMENT_ERR.
var iframe= $('myiframe');
// get window and document objects for iframe (IE compatible)
var idoc= iframe.contentDocument || iframe.contentWindow.document;
var iwin= iframe.contentWindow || iframe.contentDocument.defaultView;
Element.extend(idoc);
idoc.body.insert('<div>hello</div>'); // fail, wrong document
iwin.Element.extend(idoc); // use the copy of Prototype in the iframe's window
idoc.body.insert('<div>hello</div>'); // ok
(Note that in Firefox you won't actually get a WRONG_DOCUMENT_ERR because they deliberately fix up this error for you silently. But more complicated manipulations can quickly get you in odd, inconsistent states.)
Most JS libraries are designed to make single-document scripting easy; they hide away much of the gory detail involved in DOM manipulation, and one of the ways they do that is by eliding the document
object. But this makes them less suited to cross-document scripting, where knowing which document
to use is vital.
Cross-document scripting is already confusing. Using a JS framework that is not specifically designed to handle it (and I don't know of one that is) just makes life even weirder.
How can I access the DOM elements within an iFrame
Chrome has default security restrictions that don't allow you to access other windows from the hard disk even though they are technically the same origin. There is a flag in Chrome that can relax that security restriction (command line argument on Windows is what I remember), though I wouldn't recommend running with that flag for more than a quick test. See this post or this article for info about the command line argument.
If you run the files off a web server (even if it's a local web server) instead of your hard drive, you won't have this issue.
Or, you could test in other browsers that aren't as restrictive.
Now that you've changed the question into something different, you have to wait for an iframe window to load before you can access the content in it and you can't use jQuery's .ready()
on a different document (it doesn't work on another document).
$(document).ready(function() {
// get the iframe in my documnet
var iframe = document.getElementById("testFrame");
// get the window associated with that iframe
var iWindow = iframe.contentWindow;
// wait for the window to load before accessing the content
iWindow.addEventListener("load", function() {
// get the document from the window
var doc = iframe.contentDocument || iframe.contentWindow.document;
// find the target in the iframe content
var target = doc.getElementById("target");
target.innerHTML = "Found It!";
});
});
Test page here.
EDIT: Upon further research, I found that jQuery will do some of this work for you like this and the jQuery solution appears to work in all the major browsers:
$(document).ready(function() {
$("#testFrame").load(function() {
var doc = this.contentDocument || this.contentWindow.document;
var target = doc.getElementById("target");
target.innerHTML = "Found It!";
});
});
Test page here.
In looking at the jQuery implementation for this, all it is really doing is setting up a load
event listener on the iFrame itself.
If you want to know the nitty gritty details that went into debugging/solving the first method above:
In my trying to solve this issue, I discovered some pretty bizarre things (in Chrome) with iFrames. When you first look in the window of the iframe, there is a document and it says that its readyState === "complete"
such that you think it's done loading, but it's lying. The actual document and actual <body>
tag from the document that is being loaded via URL into the iframe is NOT actually there yet. I proved this by putting a custom attribute on the <body data-test="hello">
and checking for that custom attribute. Lo and behold. Even though document.readyState === "complete"
, that custom attribute is not there on the <body>
tag. So, I conclude (at least in Chrome) that the iFrame initially has a dummy and empty document and body in it that are not the actual ones that will be in place once the URL is loaded into the iFrame. This makes this whole process of detecting when it's ready to be quite confusing (it cost me hours figuring this out). In fact, if I set an interval timer and poll iWindow.document.body.getAttribute("data-test")
, I will see it show as undefined
repeatedly and then finally it will show up with the correct value and all of this with document.readyState === "complete"
which means it's completely lying.
I think what's going on is that the iFrame starts out with a dummy and empty document and body which is then replaced AFTER the content starts loading. On the other hand, the iFrame window
is the real window. So, the only ways I've found to actually wait for the content to be loaded are to monitor the load
event on the iFrame window
as that doesn't seem to lie. If you knew there was some specific content you were waiting for, you could also poll until that content was available. But, even then you have to be careful because you cannot fetch the iframe.contentWindow.document
too soon because it will be the wrong document if you fetch it too soon. The whole thing is pretty broken. I can't find any way to use DOMContentLoaded
from outside the iFrame document itself because you have no way of knowing then the actual document
object is in place to you can attach the event handler to it. So ... I settled for the load
event on the iFrame window which does seem to work.
If you actually control the code in the iFrame, then you can trigger the event more easily from the iFrame itself, either by using jQuery with $(document).ready()
in the iFrame code with it's own version of jQuery or by calling a function in the parent window from a script located after your target element (thus ensuring the target element is loaded and ready).
Further Edit
After a bunch more research and testing, here's a function that will tell you when an iFrame hits the DOMContentLoaded
event rather than waiting for the load
event (which can take longer with images and style sheets).
// This function ONLY works for iFrames of the same origin as their parent
function iFrameReady(iFrame, fn) {
var timer;
var fired = false;
function ready() {
if (!fired) {
fired = true;
clearTimeout(timer);
fn.call(this);
}
}
function readyState() {
if (this.readyState === "complete") {
ready.call(this);
}
}
// cross platform event handler for compatibility with older IE versions
function addEvent(elem, event, fn) {
if (elem.addEventListener) {
return elem.addEventListener(event, fn);
} else {
return elem.attachEvent("on" + event, function () {
return fn.call(elem, window.event);
});
}
}
// use iFrame load as a backup - though the other events should occur first
addEvent(iFrame, "load", function () {
ready.call(iFrame.contentDocument || iFrame.contentWindow.document);
});
function checkLoaded() {
var doc = iFrame.contentDocument || iFrame.contentWindow.document;
// We can tell if there is a dummy document installed because the dummy document
// will have an URL that starts with "about:". The real document will not have that URL
if (doc.URL.indexOf("about:") !== 0) {
if (doc.readyState === "complete") {
ready.call(doc);
} else {
// set event listener for DOMContentLoaded on the new document
addEvent(doc, "DOMContentLoaded", ready);
addEvent(doc, "readystatechange", readyState);
}
} else {
// still same old original document, so keep looking for content or new document
timer = setTimeout(checkLoaded, 1);
}
}
checkLoaded();
}
This is simply called like this:
// call this when you know the iFrame has been loaded
// in jQuery, you would put this in a $(document).ready()
iFrameReady(document.getElementById("testFrame"), function() {
var target = this.getElementById("target");
target.innerHTML = "Found It!";
});
how can I access iframe.contentDocument to get response after cross-origin request?
After digging around to try and figure this out, I finally came across a solution that seems to work for me. However, it isn't an exact answer to my question.
In summary, I'm working on supporting <form>
based file uploads. For browsers that do not support file upload via XHR
, we have to resort to the traditional <form>
submission, using a hidden <iframe>
to avoid page refresh. The form redirects the reload action into the hidden <iframe>
, and then the HTTP response is written to the body of the <iframe>
after the file has been transferred.
Due to the same-origin policy, and hence the reason I asked this question, I don't have access to the <iframe>
's content. The same-origin policy for <iframe>
s restricts how a document or script loaded from one origin can interact with a resource from another origin. Since we can't get access to the <iframe>
's document, which is where the file upload HTTP response gets written to, the server will return a redirect response to which the server will append the upload response JSON. When the <iframe>
loads, the JS will parse out the response JSON, and write it to the <iframe>
's body. Finally, since the redirect was to the same origin, we can access the <iframe>
's contents :)
A huge thanks to jQuery File Uploader; they did all the hard work ;)
https://github.com/blueimp/jQuery-File-Upload/wiki/Cross-domain-uploads
Setting Up...
JS
function setupForm() {
// form is declared outside of this scope
form = document.createElement('form');
form.setAttribute('id', 'upload-form');
form.setAttribute('target', 'target-iframe');
form.setAttribute('enctype', 'multipart/form-data');
form.setAttribute('method', 'POST');
// set the 'action' attribute before submitting the form
form.setAttribute('action', 'invalid');
};
function setupIframe() {
// iframe is declared outside of this scope
iframe = document.createElement('iframe');
/*
* iframe needs to have the 'name' attribute set so that some versions of
* IE and Firefox 3.6 don't open a new window/tab
*/
iframe.id = 'target-iframe';
iframe.name = 'target-iframe';
/*
* "javascript:false" as initial iframe src to prevent warning popups on
* HTTPS in IE6
*/
iframe.src = 'javascript:false;';
iframe.style.display = 'none';
$(iframe).bind('load', function() {
$(iframe)
.unbind('load')
.bind('load', function() {
try {
/*
* the HTTP response will have been written to the body of the iframe.
* we're assuming the server appended the response JSON to the URL,
* and did the redirect correctly
*/
var content = $(iframe).contents().find("body").html();
response = $.parseJSON(content);
if (!response) {
// handle error
return;
}
uploadFile(...); // upload the next file
}
catch (e) {
// handle error
}
});
});
/*
* insert the iframe as a sibling to the form. I don't think it really
* matters where the iframe is on the page
*/
$(form).after(iframe);
};
HTML - redirect page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<script type="text/javascript">
// grabs the JSON from the end of the URL
document.body.innerHTML = decodeURIComponent(window.location.search.slice(1));
</script>
</body>
</html>
The only thing left to do is set the action
attribute on the <form>
to the cross-domain URL that we're sending the upload to:
form.setAttribute('action', 'http://sub.example.com:8080/upload?id=ab123');
form.submit();
Meanwhile, on the server...
// send redirect to the iframe redirect page, where the above HTML lives
// generates URL: http://example.com/hack?%7B%22error%22%3Afalse%2C%22status%22%3A%22success%22%7D
response.sendRedirect("http://example.com/hack?{\"error\":false,\"status\":\"success\"}");
I know this is a massive hack to get around the same-origin policy with <iframe>
s, but it seems to work, and I think it's pretty good with cross-browser compatibility. I haven't tested it in all browsers, but I'll get around to doing that, and post an update.
Getting the document object of an iframe
Try the following
var doc=document.getElementById("frame").contentDocument;
// Earlier versions of IE or IE8+ where !DOCTYPE is not specified
var doc=document.getElementById("frame").contentWindow.document;
Note: AndyE pointed out that contentWindow
is supported by all major browsers so this may be the best way to go.
- http://help.dottoro.com/ljctglqj.php
Note2: In this sample you won't be able to access the document via any means. The reason is you can't access the document of an iframe with a different origin because it violates the "Same Origin" security policy
- http://javascript.info/tutorial/same-origin-security-policy
Related Topics
JavaScript to Get Paragraph of Selected Text in Web Page
Adding Google Translate to a Web Site
Extract the Text Out of HTML String Using JavaScript
How to Print a PDF from the Browser
Jquery Animated Number Counter from Zero to Value
Window.Getselection() Gives Me the Selected Text, But I Want the HTML
Selecting Multiple from an HTML Select Element Without Using Ctrl Key
Reading from Udp Port in Browser
Get Value of a Custom Attribute
Sending Message to a Specific Connected Users Using Websocket
HTML Drag and Drop on Mobile Devices
JavaScript Regex Returning True.. Then False.. Then True.. etc
How to Submit 2 Forms in One Page with a Single Submit Button
Completely Lost on How to Save Extension Popup Window Content
Dynamically Using the First Frame as Poster in HTML5 Video