Accessing frames via the DOM in IE - javascript

OK, every other browser works fine with the method I have coded so far but for some reason Internet Explorer will not work. I have spent hours of time (more time than actually developing the feature!) on compatibility and am close to giving up!
I have a forum and one of its neat features is the WYSIWYG editor. For that, I essentially have an IFrame that acts as the document:
<iframe name="writer" src="/scripts/blank.html" class="writer"></iframe>
This is the current state of the JavaScript (constantly updated):
function initEditor()
{
w = frames['writer']
wc = g('writerCopy')
if(w == null) return
frames['writer'].document.designMode = 'on'
frames['writer'].document.body.innerHTML = styleSheet+wc.value
frames['writer'].focus()
}
It works partially now, but fails on the line:
frames['writer'].document.body.innerHTML = styleSheet+wc.value
in Internet Explorer with "'frames.writer.document.body' is null or not an object".

I'm not even sure IE supports that designMode.
And, .contentDocument is only IE8, IE7 and less uses .contentWindow.document, but iframe windows are part of the frames-collection.
try this, should be crossbrowser:
<iframe name="writer"></iframe>
frames["writer"].document.body.innerHTML = "some html...";

You need to point your iframe to a dummy document for IE. Just create a file blank.html with the following:
<html><body></body></html>
and set <iframe src="blank.html" ... >
Then you can go about referencing frame.document.body.innerHTML = '...' to your hearts content.
BTW that is a terrible title to a question.

Evidently IE8 does not make frame elements available until the entire parent page has loaded. Also note, you can write to the frame before the parent page loads, but this will overwrite the frame and prevent it from being loaded.
The easy solution is to move the InitEditor() call from inside the body to here:
<body onload="InitEditor()">

Perhaps the iframe isn't loaded yet. I can duplicate your "'frames.writer.document.body' is null or not an object" error. I added a setTimeout around it and it then worked for me.
setTimeout(function () {
frames['writer'].document.body.innerHTML = "some text";
}, 200);

Have you activated IE's debugging facilities?

Am I missing something here? shouldn't you use something like:
window.frames[nameOrNumberOfFrame]...
See also in MSDN:
This collection contains only window
objects and does not provide access to
the corresponding frame and iframe
objects. To access these objects, use
the all collection for the document
containing the objects.

In the end I used frames['frameName'].document.write('someText') but only if the other method fails.

Related

jQuery 2.0.3 bug - fadeIn(), show() broken in firefox - SecurityError: The operation is insecure

I have a very basic html element that I would like to fadeIn(). I am however using require.js so I believe this could be part of the problem. I am using jQuery 2.0.3 When using fadeIn I get this error:
SecurityError: The operation is insecure.
chrome://firebug/content/console/commandLineExposed.js
Line 5
I have never seen this before, I have reset firefox and my PC.
Html
<message-box>
<message-info></message-info>
<close-box>x</close-box>
</message-box>
JS
$('message-Box').fadeIn();
I only get this error with firefox v27. No other browsers are having this problem, but I haven't tested it in any older versions of FF
I am not seeking help for anything other than the error...
See the error in action? and run this command: SD.message.showMessage('Somehow this breaks everything', 'bad');
-----Edit-------
So sadly you'll need to test this Here I assure you this is SFW, its just the sign in page.
I am confident there must be something in my other JS files that is conflicting, but I, as yet, have not found the problem.
I removed a fiddle that was here as it in no way helped the question, since adding the bounty I want it to be as helpful as possible.
Second Edit
Oddly, when running any show(), hide(), fadeIn() etc an iframe is created at the base of the page, just before the body. I'll need to have a think in my code why this would be happening.
Third Edit
I have no reason or explanation for this, but updating to jQuery 2.1.0 has fixed my issues. If anybody can explain the problem then I'd love to give them the points :)
Stepping through the jQuery code, you eventually hit this internal function below. The security error is thrown when jQuery attempts to write to the iframe document. jQuery 2.1.0 has a different way of determining the default node display value so you can just treat this as a jQuery/browser combo bug. You can minimally recreate the security error by pasting the following into the console:
var iframe = jQuery("<iframe frameborder='0' width='0' height='0'/>").css( "cssText", "display:block !important" ).appendTo(document.documentElement);
iframe[0].contentWindow.document.write("<!doctype html><html><body>");
Internal jQuery function:
function css_defaultDisplay( nodeName ) {
var doc = document,
display = elemdisplay[ nodeName ];
if ( !display ) {
display = actualDisplay( nodeName, doc );
// If the simple way fails, read from inside an iframe
if ( display === "none" || !display ) {
// Use the already-created iframe if possible
iframe = ( iframe ||
jQuery("<iframe frameborder='0' width='0' height='0'/>")
.css( "cssText", "display:block !important" )
).appendTo( doc.documentElement );
// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
doc.write("<!doctype html><html><body>");
doc.close();
display = actualDisplay( nodeName, doc );
iframe.detach();
}
// Store the correct default display
elemdisplay[ nodeName ] = display;
}
return display;
}
As per specification custom elements shall have '-' in their tags, so your markup should look like this:
<message-box>
<x-message><div></div></x-message>
<x-close>x</x-close>
</message-box>
After the change and corresponding style updates it works as far as I can tell: http://jsfiddle.net/9Frn8/11/
Looks like this may be due to the absolute paths in your CSS file. I also see (in console) you are trying to do a call to localhost (which fails, of course). There seems to be some issues in your code that is causing Firefox to stop specific processes. Specifically, something that firefox considers cross-domain.
This is most likely a Same-Origin-Policy issue.

Why is iframe.contentWindow == null?

I use the following code to dynamically create an iframe.
var iframe_jquery = $("<iframe>")
.addClass("foo")
.appendTo(container); // container is a jQuery object containing a <div> which already exists
Then, I want to access its contentWindow, but it's null:
var iframe = iframe_jquery.get(0);
if (iframe){ // iFrame exists
console.log(iframe.contentWindow); // Prints "null"
var doc = iframe.contentWindow.document; // NullpointerException
}
So I thought: "Maybe the iframe isn't ready yet?" So I tried:
iframe_jquery.ready(function(){
var iframe = iframe_jquery.get(0);
console.log(iframe.contentWindow); // Prints "null"
var doc = iframe.contentWindow.document; // NullpointerException
});
Same result.
What's wrong?
I had this problem last week while playing with iframes (building an rtf editor), and yeah it's not ready yet.
I thought if I put it in a .ready(), it would work, but .ready() is when the DOM is ready, not when the iframe has loaded its contents, so I ended up wrapping my code with jQuery .load().
So try this:
$(function () {
$("#myiframe").load(function () {
frames["myframe"].document.body.innerHTML = htmlValue;
});
});
Hope this helps
The problem is that your <iframe> won't be "real" until it's really added to the actual DOM for the page. Here is a fiddle to demonstrate..
Depending on the browser, accessing the document or an <iframe> may vary.
Here is an example of how to handle it:
if (iframe.contentDocument) // FF Chrome
doc = iframe.contentDocument;
else if ( iframe.contentWindow ) // IE
doc = iframe.contentWindow.document;
You can also make a function that will be executed when the iframe has finished loading by setting it's onload attribute.
Bookmarklet version
Just out of curiosity I thought I'd put this together. Remembering that iframes and load events don't play well together on different browsers (mainly older, falling apart, should-be-dead browsers)... plus not being entirely sure how jQuery gets around this problem... my brain decided that this would be better supported (whether it is or not is neither here nor there):
$(function(){
/// bind a listener for the bespoke iframeload event
$(window).bind('iframeload', function(){
/// access the contents of the iframe using jQuery notation
iframe.show().contents().find('body').html('hello');
});
/// create your iframe
var iframe = $('<iframe />')
/// by forcing our iframe to evaluate javascript in the path, we know when it's ready
.attr('src', 'javascript:(function(){try{p=window.parent;p.jQuery(p).trigger(\'iframeload\');}catch(ee){};})();')
/// insert the iframe into the live DOM
.appendTo('body');
});
The reason for taking this approach is that it is normally far better to trigger your load event from inside the iframe itself. But this means having a proper document loaded in to the iframe, so for dynamic iframes this is a little tedious. This is kind of a mixture between having a document loaded, and not.
The above works on everything I have tested so far - and yes you are correct - it is a little ridiculous, non-future-proof and propably other things that have negative connotations ;)
One positive thing I'll say about this post is that introduces the use of .contents() to access the document of the iframe, which is at least a little bit useful...

Create an iframe then append data to it with jQuery

I am trying do some modification to an greasemonkey userscript to implement a feature I need. The code is like
showAddress:function(addrString,type)
{
this.addrBox=$('<div id="batchPublish"></div>')
.append('<div id="batchHeader"></div>')
.append('<div id="batchContent" style="float:left;clear:both"></div>');
.........
var batchContent=this.addrBox.find('#batchContent')
.append('<pre width="300" style="text-align:left" id="batchedlink"></pre>');
this.addrBox.find('#batchedlink').css({'width':'500px','height':'250px','overflow':'auto','word-wrap': 'break-word'})
.append(addrString);
$.blockUI({message:this.addrBox,css:{width:"520px",height:"300px"}}); }
Basically this code writes data to html. What I want to implement is to have "addrString" written to an iframe embedded. Now It's in the "pre" tag. I have tried many approaches but still no luck. Iframe was always empty.
I am completely a novice in javascript and unclear whether this is possible.
Thank you for the help.
Since you are adding the iFrame in the same domain, then you can manipulate its contents like this:
(See it in action at jsBin.)
$("#batchContent").append ('<iframe id="batchedlink"></iframe>');
/*--- Compensate for a bug in IE and FF, Dynamically added iFrame needs
some time to become "DOM-able".
*/
setTimeout ( function () {
var iframeBody = $("#batchedlink").contents ().find ("body");
iframeBody.append (addrString);
},
333
);
NOTE:
For a Chrome userscript, you apparently don't need the timer delay. But for FF and IE 8 (the other 2 browsers I double-checked), a dynamically added iFrame is not manipulable until after it has "settled" for some reason. This seems to take about 200 mS.
A statically loaded iFrame does not have this lag, see the jsBin demo.
Sort of hard to tell exactly what you're asking -- but if you want to know whether or not you can append DOM elements to an iFrame, the answer is "no".

set innerHTML of an iframe

In javascript, how can I set the innerHTML of an iframe? I mean: how to set, not get.
window["ifrm_name"].document.innerHTML= "<h1>Hi</h1>" does not work, and the same for other solutions.
Iframe and parent document are on the same domain.
I would need to set html of the whole document iframe, not its body.
I would need to avoid jquery solution.
A really simple example ...
<iframe id="fred" width="200" height="200"></iframe>
then the following Javascript is run, either inline, part of an event, etc ...
var s = document.getElementById('fred');
s.contentDocument.write("fred rules");
the "contentDocument" is the equivalent of the "document" you get in the main window, so you can make calls against this to set the body, head, any elements inside ... etc.
I've only tested this in IE8, Chrome and Firefox ... so you may want to test in IE6/7 if you have copies available.
In Firefox and Chrome (don't know about Opera), you can use the data: URI scheme.
<iframe src=".... data: URI data here ......">
JSFiddle example
Here is a tool to generate data:URI encoded data.
This does not work in IE:
For security reasons, data URIs are restricted to downloaded resources. Data URIs cannot be used for navigation, for scripting, or to populate frame or iframe elements.
If however as you say in the comment, getting/setting the document's body is enough, you are much easier off using one of the linked examples.
There is also the srcdoc attribute:
<iframe srcdoc="<p><h1>Hello</h1> world</p>"></iframe>
Demo, Polyfill.
In improving my file uploads in an AJAXS env I had the same need. This worked for me in ie8 and ff 22.0. Both the body innerhtml and div innerhtml work.
function copyframedata(data) {
var x = document.getElementById("photo_mgr_frame");
var y = x.contentWindow || x.contentDocument;
if (y.document) y = y.document;
y.getElementById('photo_mgr_mb').innerHTML = data;
}
got it from w3
I came across the same problem but here's an easy fix.
function Run(){
var txt = "<h1>Hello World</h1>";
var frame = document.getElementById('frame');
var frame = (frame.contentWindow || frame.contentDocument);
if (frame.document) frame = frame.document;
frame.open();
frame.write(txt);
frame.close();
}
<iframe id='frame'>
</iframe>
<button onclick='Run()'>Run</button>

Remove all content using pure JS

I'm looking for a way to remove the entire content of a web page using pure Javascript -- no libraries.
I tried:
document.documentElement.innerHTML = "whatever";
but that doesn't work: it replaces the inside of the <html/> element. I'm looking at replacing the entire document, including if possible the doctype and <?xml declaration.
I think a browser rightfully assumes a page with content-type text/html will always be a web page - so whilst you may do something like...
document.body.innerHTML = '';
It will still have some HTML hanging around.
You could try...
document.documentElement.innerHTML = '';
...which left me with <html></html>.
Yi Jiang did suggest something clever.
window.location = 'about:blank';
This will take you to a blank page - an internal mechanism provided by most browsers I believe.
I think however the best solution is to use document.open() which will clear the screen.
var i = document.childNodes.length - 1;
while (i >= 0) {
console.log(document.childNodes[i]);
document.removeChild(document.childNodes[i--]);
}
Removes everything (doctype also) on FF 3.6, Chrome 3.195, and Safari 4.0. IE8 breaks since the child wants to remove its parent.
Revisiting a while later, could also be done like this:
while (document.firstChild) {
document.removeChild(document.firstChild);
}
According to Dotoro's article on the document.clear method, they (since it's deprecated) recommend calling document.open instead, which clears the page, since it starts a new stream.
This way, you avoid the nasty about:blank hack.
One can remove both the <html> element (document.documentElement) and the doctype (document.doctype).
document.doctype.remove();
document.documentElement.remove();
Alternatively, a loop can be used to remove all children of the document.
while(document.firstChild) document.firstChild.remove();
document.open() or document.write() work as well.
After the page has already fully loaded:
document.write('');
document.close();
I believe this will do it
document.clear() //deprecated
window.location = "about:blank" //this clears out everything
I believe this will still leave the doctype node hanging around, but:
document.documentElement.remove()
or the equivalent
document.getElementsByTagName("html")[0].remove()
document.documentElement.innerHTML='';
document.open();
The Document.open() method opens a document for writing.
if you dont use open method, you cant modify Document after set innerhtml to empty string
Live demo
If youre using jQuery here's your solution
<div id="mydiv">some text</div>
<br><br>
<button id="bn" style="cursor:pointer">Empty div</button>
<script type="text/javascript">
$(document).on('click', '#bn', function() {
$("#mydiv").empty();
$("#bn").empty().append("Done!");
});
</script>
If youre using javascript here's your solution
<div id="purejar">some text</div>
<br><br>
<button id="bnjar" onclick="run()" style="cursor:pointer">Empty div</button>
<script type="text/javascript">
var run = function() {
var purejar = document.getElementById("purejar");
var bn = document.getElementById("bnjar");
purejar.innerHTML = '';
bn.innerHTML = 'Done!';
}
</script>
Im just curious as to why you'd want to do that. Now theres no way that I know of to replace absolutely everything down to the doctype declaration but if you are wanting to go to those lengths why not redirect the user to a specially crafted page that has the template you need with the doctype you need and then fill out the content there?
EDIT: in response to comment, what you could do is strip all content then create an iframe make it fill the entire page, and then you have total control of the content. Be aware that this is a big hack and will probably be very painful - but it would work :)
REMOVE EVERYTHING BUT --- !DOCTYPE html ---
var l = document.childNodes.length;
while (l > 1) { var i = document.childNodes[1]; document.removeChild(i); l--; }
TESTED ON FIREFOX WEB INSPECTOR - childNodes[1] IS --- !DOCTYPE html ---

Categories

Resources