I would like to globally redefine a createElement method, but unfortunately it only works in a main document, and is ignored in iframes. Let's take this example code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
old_createElement = document.createElement;
document.createElement = function(el_type) {
new_el = old_createElement.call(this, el_type);
new_el.style.color="red";
return new_el;
};
</script>
</head>
<body bgcolor="white">
<iframe id="iframe1"></iframe>
<script type="text/javascript">
window.setTimeout(function(){
iframe_el = document.getElementById("iframe1").contentDocument.createElement("div");
iframe_el.innerHTML = 'inside iframe';
document.getElementById("iframe1").contentDocument.body.appendChild(iframe_el);
},50);
no_iframe_el=document.createElement('div');
no_iframe_el.innerHTML = 'outside of iframe';
document.body.appendChild(no_iframe_el);
</script>
</body>
</html>
When i open in in a browser, the element created in a main document has red color, as expected, but the one in the iframe is black.
The problem is that I only have control on the script contained in the HEAD section of the document. In other words, i don't know how many iframes there will be later on in the HTML source, or how they will be names, or if they are added via user's Javascript.
My question is: how can i change the method globally, so all elements created in iframes also use this new style?
Thanks a lot!
Each frame has it's own separate Javascript context. If you want to change that frame's context, you have to do it specifically for that frame.
In your specific example, each frame has its own document object so it should be no surprise that each document has its own .createElement property.
You cannot generically change things in a way that will affect all frames. And, in fact if it's a cross-origin frame, you can't change it at all.
Related
Consider the script..
<html>
<head>
<script type="text/javascript">
document.write('TEST');
</script>
</head>
<body>
Some body content ...
</body>
</html>
This works fine and the word 'TEST' is added to the <body>
But when
<script type="text/javascript">
window.onload = function(){
document.write('TEST');
}
</script>
is used, then the body content is fully replaced by the word 'TEST' i.e, the old body contents are removed and ONLY the word 'TEST' is added.
This happens only when document.write is called within window.onload function
I tried this in chrome. Is there any mistake made by me ? any suggestions ?
document.write() is unstable if you use it after the document has finished being parsed and is closed. The behaviour is unpredictable cross-browser and you should not use it at all. Manipulate the DOM using innerHTML or createElement/createTextNode instead.
From the Mozilla documentation:
Writing to a document that has already loaded without calling document.open() will automatically perform a document.open call. Once you have finished writing, it is recommended to call document.close(), to tell the browser to finish loading the page. The text you write is parsed into the document's structure model. In the example above, the h1 element becomes a node in the document.
If the document.write() call is embedded directly in the HTML code, then it will not call document.open().
The equivalent DOM code would be:
window.onload = function(){
var tNode = document.createTextNode("TEST");
document.body.appendChild(tNode);
}
in the first case the word is not written in the body .. it is written in the head
the first one works because the document is still open for writting.. once it has completed (DOM loaded) the document is closed, and by attempting to write to it you replace it ..
When document is full loaded, any further call to document.write() will override document content. You must use document.close() before calling document.write() to avoid overwriting.
First create an element, for example a div, than add content to the div with window.onload event.
document.write('<div id="afterpostcontent"><\/div>');
window.onload = function()
{
document.getElementById('afterpostcontent').innerHTML = '<span>TEST<\/span>';
}
You can create an external JavaScript file with this content and just call it anywhere, for example:
<script src="afterpostcontentcode.js"></script>
So i have this empty page,
<html>
<head>
<title>JS</title>
<script type="text/javascript">
var container = document.getElementById("container");
console.log(container);
</script>
</head>
<body>
<div id="container"></div>
</body>
</html>
and for some reason console.log returns "null" instead of div object. But when i put code onto some website like jsfiddle, it works.
How do i fix this, is it a common problem?
JSFiddle may not mimic DOM loading correctly. The problem with your example is your JavaScript is executed before the DOM is completely loaded. The DOM is loaded top-down, so when your JavaScript is executed, the container div doesn't exist in the DOM yet.
You can move your script block after your div, that's a quick way to resolve the issue. Alternatively, you can listen for an event for when the DOM is loaded, then execute your code. This StackOverflow question demonstrates how to do that.
I'm writing a GreaseMonkey script that modifies an attribute of an element with a specific ID, but I'm having some problems accessing it due to a nontraditional HTML hierarchy. Here's the relevant HTML:
<body>
...
<iframe id="iframeID">
<html>
...
<body id="bodyID" attribute="value">
...
</body>
...
</html>
</iframe>
...
</body>
Where attribute is the attribute that I'm attempting to modify.
At first, not realizing I was working with an iframe and a nested body tag, I tried this:
document.getElementById('bodyID').setAttribute("attribute","value")
While this worked fine in Firefox, Chrome tells me that I can't set the attribute of null, suggesting that it cannot find any elements with the id bodyID. How can I modify this attribute in a cross-browser friendly fashion?
You first need to pull the document of the <iframe>:
document.getElementById('iframeID').contentDocument
.getElementById('bodyID').setAttribute("attribute","value");
Live DEMO
BTW, if you want to get the <body> node, you don't need to give id or something like that, simply:
document.body
In your case, it's the document of the <iframe>:
document.getElementById('iframeID').contentDocument.body.setAttribute("attribute","value");
A lot simpler... Isn't it?
The best way, IMO, to do this is to listen for the load event fired by the iFrame, then look into the iFrame DOM as need. This guarantees that you'll have the iFrame DOM available when you need it and take a while shot.
$('#iframeID').on('load', function ()
{
alert('loaded'); // iFrame successfully loaded
var iFrameDoc = $('#iframeID')[0].contentDocument; // Get the iFrame document
$('body', iFrameDoc).attr('attribute', 'new value'); // Get the 'body' in the iFrame document and set 'attribute' to 'new value'
});
A key screen in my application involves a HTML page with several iframes performing various functions. All of these will need access to jQuery. In the interest of speeding up loading time, and reducing HTTP traffic (though I know caching could help with this), it would be good if the jQuery code itself were loaded only once. If my page is like so
<html>
<head>
<script src="jquery-1.5.js" type="text/javascript"></script>
</head>
<body>
<iframe src="other.html"></iframe>
<div id="foo"> ... stuff here ... </div>
</body>
</html>
Then inside other I can refer to parent.$('#foo') but that will refer to the parent's "foo" element, not the child's. Is there a way to use the parent's "instance" of jQuery on the child document or elements in it?
Have you tried:
var $ = window.parent.$;
$('#foo'); // parent's frame's #foo, is the same as "window.parent.$('#foo');"
and:
var $ = window.parent.$;
$('#foo', document); // this frame's #foo because the current document is given as context
Make sure each IFRAME references the exact same version of jquery. It should only get loaded once and be cached by the browser. IFRAMES don't have access to scripts of their parents.
You might also want to consider using a CDN for Jquery (such as Google's CDN) to improve performance.
We solved it with:
var window.jQuery = window.$ = function( selector, context ) {
return window.parent.jQuery(selector, context?context:window.document)
}
I have this HTML code:
<html>
<head>
<script type="text/javascript">
function GetDoc(x)
{
return x.document ||
x.contentDocument ||
x.contentWindow.document;
}
function DoStuff()
{
var fr = document.all["myframe"];
while(fr.ariaBusy) { }
var doc = GetDoc(fr);
if (doc == document)
alert("Bad");
else
alert("Good");
}
</script>
</head>
<body>
<iframe id="myframe" src="http://example.com" width="100%" height="100%" onload="DoStuff()"></iframe>
</body>
</html>
The problem is that I get message "Bad". That mean that the document of iframe is not got correctly, and what is actualy returned by GetDoc function is the parent document.
I would be thankful, if you told where I do my mistake. (I want to get document hosted in IFrame.)
Thank you.
You should be able to access the document in the IFRAME using the following code:
document.getElementById('myframe').contentWindow.document
However, you will not be able to do this if the page in the frame is loaded from a different domain (such as google.com). This is because of the browser's Same Origin Policy.
The problem is that in IE (which is what I presume you're testing in), the <iframe> element has a document property that refers to the document containing the iframe, and this is getting used before the contentDocument or contentWindow.document properties. What you need is:
function GetDoc(x) {
return x.contentDocument || x.contentWindow.document;
}
Also, document.all is not available in all browsers and is non-standard. Use document.getElementById() instead.
In case you get a cross-domain error:
If you have control over the content of the iframe - that is, if it is merely loaded in a cross-origin setup such as on Amazon Mechanical Turk - you can circumvent this problem with the <body onload='my_func(my_arg)'> attribute for the inner html.
For example, for the inner html, use the this html parameter (yes - this is defined and it refers to the parent window of the inner body element):
<body onload='changeForm(this)'>
In the inner html :
function changeForm(window) {
console.log('inner window loaded: do whatever you want with the inner html');
window.document.getElementById('mturk_form').style.display = 'none';
</script>
You can also use:
document.querySelector('iframe').contentDocument