I desperately need some help on this one.
I've created a <script> that closely parallels, and reproduces the problem of, another more complex <script> that I've written elsewhere.
Here's what it does:
The three files involved are:
This all works in Firefox, Safari, and Chrome. Where it breaks down is in Internet Explorer and Opera. What happens is that the render() function in main.js executes, and all three alerts are fired, but the document in the <iframe> is not overwritten. I can't tell what document is being created or written to, or if one is at all.
If I add debug code (like console.log(document)) in the beginning of the render() function, the working browsers seem to get a handle on the existing <iframe> document and list the properties included below. Internet Explorer also appears to find a document of some sort. I just can't tell why it's not letting me overwrite it.
Could it be an issue of scope? Maybe I'm using the document.write(), document.open() or document.close() methods improperly, and Firefox and a few other browsers are just letting me get away with it?
One possible clue: if I take the guts of the render() function out (i.e., just put them after load() in main.js), this works fine. That suggests to me that it's not how I'm using document.open(), etc., but that somehow by the time that the callback() function is executed, the document object is not available, or has gone out of scope, or something like that.
This has me totally stumped, and it's for a very important project with an impending deadline. I'm not above a hack or workaround if it gets me out of this jam. Any help or insight would be EXTREMELY appreciated!
console.log()'s listing of the document properties:
ATTRIBUTE_NODE: 2 CDATA_SECTION_NODE: 4 COMMENT_NODE: 8 DOCUMENT_FRAGMENT_NODE: 11 DOCUMENT_NODE: 9 DOCUMENT_POSITION_CONTAINED_BY: 16 DOCUMENT_POSITION_CONTAINS: 8 DOCUMENT_POSITION_DISCONNECTED: 1 DOCUMENT_POSITION_FOLLOWING: 4 DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 32 DOCUMENT_POSITION_PRECEDING: 2 DOCUMENT_TYPE_NODE: 10 ELEMENT_NODE: 1 ENTITY_NODE: 6 ENTITY_REFERENCE_NODE: 5 NOTATION_NODE: 12 PROCESSING_INSTRUCTION_NODE: 7 TEXT_NODE: 3 URL: "http://localhost/projects/test/ajax_loader/document_write/index.html" activeElement: HTMLBodyElement addEventListener: function addEventListener() { adoptNode: function adoptNode() { alinkColor: "" all: HTMLCollection anchors: HTMLCollection appendChild: function appendChild() { applets: HTMLCollection attributes: null baseURI: "http://localhost/projects/test/ajax_loader/document_write/index.html" bgColor: "" body: HTMLBodyElement captureEvents: function captureEvents() { characterSet: "UTF-8" charset: "UTF-8" childNodes: NodeList clear: function clear() { cloneNode: function cloneNode() { close: function close() { compareDocumentPosition: function compareDocumentPosition() { compatMode: "BackCompat" cookie: "__gads=ID=62bb88ab20ac9451:T=1256683145:S=ALNI_Mbso-nFjAvRzYhCSwhiuaDh84G8CA" createAttribute: function createAttribute() { createAttributeNS: function createAttributeNS() { createCDATASection: function createCDATASection() { createComment: function createComment() { createDocumentFragment: function createDocumentFragment() { createElement: function createElement() { createElementNS: function createElementNS() { createEntityReference: function createEntityReference() { createEvent: function createEvent() { createExpression: function createExpression() { createNSResolver: function createNSResolver() { createNodeIterator: function createNodeIterator() { createProcessingInstruction: function createProcessingInstruction() { createRange: function createRange() { createTextNode: function createTextNode() { createTreeWalker: function createTreeWalker() { defaultCharset: "iso-8859-1" defaultView: DOMWindow designMode: "off" dir: "" dispatchEvent: function dispatchEvent() { doctype: null documentElement: HTMLHtmlElement documentURI: "http://localhost/projects/test/ajax_loader/document_write/index.html" domain: "localhost" elementFromPoint: function elementFromPoint() { embeds: HTMLCollection evaluate: function evaluate() { execCommand: function execCommand() { fgColor: "" firstChild: HTMLHtmlElement forms: HTMLCollection getCSSCanvasContext: function getCSSCanvasContext() { getElementById: function getElementById() { getElementsByClassName: function getElementsByClassName() { getElementsByName: function getElementsByName() { getElementsByTagName: function getElementsByTagName() { getElementsByTagNameNS: function getElementsByTagNameNS() { getOverrideStyle: function getOverrideStyle() { getSelection: function getSelection() { hasAttributes: function hasAttributes() { hasChildNodes: function hasChildNodes() { hasFocus: function hasFocus() { height: 150 images: HTMLCollection implementation: DOMImplementation importNode: function importNode() { inputEncoding: "UTF-8" insertBefore: function insertBefore() { isDefaultNamespace: function isDefaultNamespace() { isEqualNode: function isEqualNode() { isSameNode: function isSameNode() { isSupported: function isSupported() { jQuery1258269389622: 2 lastChild: HTMLHtmlElement lastModified: "" linkColor: "" links: HTMLCollection localName: null location: Location lookupNamespaceURI: function lookupNamespaceURI() { lookupPrefix: function lookupPrefix() { namespaceURI: null nextSibling: null nodeName: "#document" nodeType: 9 nodeValue: null normalize: function normalize() { open: function open() { ownerDocument: null parentElement: null parentNode: null plugins: HTMLCollection preferredStylesheetSet: null prefix: null previousSibling: null queryCommandEnabled: function queryCommandEnabled() { queryCommandIndeterm: function queryCommandIndeterm() { queryCommandState: function queryCommandState() { queryCommandSupported: function queryCommandSupported() { queryCommandValue: function queryCommandValue() { querySelector: function querySelector() { querySelectorAll: function querySelectorAll() { readyState: "complete" referrer: "http://localhost/projects/test/ajax_loader/document_write/index.html" releaseEvents: function releaseEvents() { removeChild: function removeChild() { removeEventListener: function removeEventListener() { replaceChild: function replaceChild() { scripts: HTMLCollection selectedStylesheetSet: null styleSheets: StyleSheetList textContent: null title: " Page" vlinkColor: "" width: 300 write: function write() { writeln: function writeln() { xmlEncoding: null xmlStandalone: false xmlVersion: null
The document. write() method writes a string of text to a document stream opened by document.
write is considered a browser violation as it halts the parser from rendering the page. The parser receives the message that the document is being modified; hence, it gets blocked until JS has completed its process. Only at this time will the parser resume.
The write() method in HTML is used to write some content or JavaScript code in a Document. This method is mostly used for testing purpose. It is used to delete all the content from the HTML document and inserts the new content. It is also used to give the additional text to an output which is open by the document.
if I take the guts of the render() function out (i.e., just put them after load() in main.js), this works fine.
Doesn't for me in IE8. If I lose the AJAX call completely and just call render()
in main.js, I get the same result. In fact even if I replace the whole of main.js with just:
document.write('hello!');
with or without opening the document, the hello never appears!
If I put any timeout (even 0) on the call to render
in main.js, it works. The timeout on the parent document, on the other hand, doesn't seem to be doing anything.
This extreme weirdness is caused by jQuery using a temporarily-inserted <script>
tag to execute the code returned by in jsonp.js. If you simply call eval
on the return value instead of having jQuery execute it, it works fine.
A related problem I found narrowing down the hello example is demonstrated by index.html:
<body>
<iframe name="foo"></iframe>
<script>
var idoc= frames['foo'].document;
idoc.open();
idoc.write('<body><script src="main.js"><\/script>');
idoc.close();
</script>
with main.js containing:
document.write('foo');
There is no foo written. (An inline script, on the other hand, was fine.)
If the idoc.close
was omitted, it worked. If an additional idoc.write('bar')
was added, the bar
was written before foo
in IE only. If I added both bar
and the close
call, IE crashed.
So to summarise, there are deep problems using document.write
from inside a document that was itself written by document.write
! Try to avoid it if at all possible. document.open
can be a useful way to populate an iframe from a parent document, but you shouldn't really be needing it inside the child document, where you can use DOM methods on yourself.
As most people have already covered, IE has serious issues when attempting to do something simple like:
var doc = window.frames['your_frame'].document;
doc.open();
doc.write('<body><script src="external_resource.js"><\/script>');
doc.close();
After exhaustive searching, I found this this write-up which suggests using the javascript: URI scheme to insert the content into the iframe document. I've personally tested this solution in current versions of FF, Chrome, & Safari, as well as IE 7/8/9/10.
Borrowing from his examples, the following would work in place of my example above, and could be used to achieve what you're going after:
var iframe = window.frames['your_frame'];
var content = '<body><script src="external_resource.js"><\/script>';
iframe.contentWindow.contents = content;
iframe.src = 'javascript:window["contents"]';
I've heard it told that older versions of Safari don't handle the javascript: URI scheme very well, but I couldn't find a way to confirm as everyone using Safari seems to upgrade regularly. I also don't know if this runs into character limit problems for the URI in IE (since it is smaller than every other browser), but it's worth looking into for those of you suffering from a similar problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With