Object.defineProperty not changing property of element - javascript

I am trying to override the src property of all iframes in my application so their src property always gets set to "redirect.html" regardless of what value the HTML tag defines for it.
So far, I have come up with the following, but it doesn't seem to be applying to the DOM element:
<!doctype html>
<html>
<head>
<script>
var propertyDescriptorSrc = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, "src");
Object.defineProperty(HTMLIFrameElement.prototype, "src", {
get: function get_src() {
var val = propertyDescriptorSrc.get.call(this);
return "redirect.html";
},
set: function (val) {
alert('setting: ' + val);
propertyDescriptorSrc.set.call(this, val);
}
});
</script>
</head>
<body>
<iframe src="page.html"></iframe>
</body>
</html>
I expected the iframe element in the body to load redirect.html instead of page.html, since I overrided its "getter", but it still loaded page.html.
Is there a way to force this behavior where all iframes by default go to redirect.html instead of whatever is defined in their src attribute?
(This is just an experimental project)

Before it starts javascript, the DOM tree is already parsed and contains all iframes together with its src, according to the Critical Rendering Path.
The only way to do this is by using javascript to redefine the src attributes of the individual iframe node. For example, as below.
all iframes are set to redirect.html:
<!doctype html>
<html>
<head>
</head>
<body>
<iframe src="page.html"></iframe>
<iframe src="page2.html"></iframe>
<script>
( function(){
var lng = document.getElementsByTagName('iframe').length;
for (var i=0; i<lng;i++){
document.getElementsByTagName('iframe')[i].src="redirect.html";
}
})();
</script>
</body>
</html>
According to the suggestion, #Aaron Digulla gives a more readable form of function.
It seems that the search algorithms of the DOM tree are so efficient today that the argument is the readability of the record, not the efficiency.
(function(){
var frames = document.getElementsByTagName('iframe');
for (var i=0; i<frames.length;i++){
frames[i].src="redirect.html";
}
})();

Related

Calling iframe link from other js file not working

I created an iframe in vex6.html and a js file in styles.gq/test.js and in that file there is the link and id but when I try to call the code in html it does not work.
Here is my code for vex6.html
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<script type="text/javascript" src="https://styles.gq/test.js" ></script>
<iframe id="vex6" width="100%" height="500" style="border:1px solid black;"></iframe>
</body>
</html>
here is my js code
document.getElementById("vex3").src
= "https://www.obviousplays.tk/gfiles/vex3";
document.getElementById("vex4").src
= "https://www.obviousplays.tk/gfiles/vex4";
document.getElementById("vex5").src
= "https://www.obviousplays.tk/gfiles/vex5";
document.getElementById("vex6").src
= "https://www.obviousplays.tk/Gfiles6/vex6";
document.getElementById("slope").src
= "https://www.obviousplays.tk/gfiles/slope";
I expected an iframe but instead there seems to be no link for the iframe
it is also spitting out the error cannot set properties of null (setting(src))
Add the line
<script type="text/javascript" src="https://styles.gq/test.js</script> after the iframe tag.
Worked for me.
Error in console: "script.js:3 Uncaught TypeError: Cannot set properties of null (setting 'src')"
Your script is loaded before the iframe content, meaning that you're trying to initialize the source of an unidentified object.
Solution: Place the <script> tag below your iframe. It is a good practice to place <script> tags at the bottom of the <body> tag so we can always access loaded content. However, sometimes it takes the contents a little bit more time until they fully load (despite placing the <script> tag at the bottom of the page). I'd suggest wrapping your code in window.onload = () {} It will ensure that all contents are loaded before firing the code.
<body>
<iframe id="vex3" width="100%" height="500" style="border:1px solid black;"></iframe>
<script type="text/javascript" src="https://styles.gq/test.js"></script>
</body>
Your code is fine and should work (pardon for criticizing). Anytime you seem to use repetitive code, it means that it can be simplified/automated like so:
window.onload = () => {
// Target all <iframe> tags.
iframes = document.querySelectorAll('iframe');
// Loop through list of <iframe> nodes.
Array.from(iframes).map(iframe => {
// Update <iframe>'s attribute "src" to the origin URL,
// and use the iframe's attribute "id" value as the final source.
iframe.setAttribute('src', `https://www.obviousplays.tk/gfiles/${iframe.id}`)
});
}

Adding a <script> node on top of <body> from javascript in <head>

I want to insert some javascript code,
That should be run before other javascript codes in the <body>run.
As they manipulate html in <body>, that are inside <body>.
Normally i would put this javascript in a <script> tag right after the opening <body> tag. But I am not writing the html directly. Its generated for me by a program. (react-storybook). Its API allows to inject html inside <head> but not <body>.
<head>
<script></script> <-- I can inject a script tag here
</head>
<body>
<script></script> <-- I can't directly add this script tag but I need one here
<script>other js</script>
</body>
I tried putting my js in a document load event handler, but they run after body is completely loaded so other js has already run.
I tried putting my js directly in the head then my js can't use appendChild on body, because at that point document.body is null.
Is there a way to insert a script tag satisfying both above requirements with accessing only the <head>?
I don't see any way to do this without probably breaking the tool you're trying to work within and doing some fairly nasty things. I suspect there's a better way to solve your underlying problem.
I tried putting my js directly in the head then my js can't use appendChild on body, because at that point document.body is null...
It's nasty and probably a bad idea, but you could force the body to be started:
document.write("<body>");
At that point, the parser will create the body element. You can then use appendChild (or just continue using the evil document.write). When the body is started again later in the normal course fo things, that second opening tag will be ignored.
It's not a good idea. react-storybook is open source. If there isn't a way to achieve your actual goal with it already, I suggest modifying it so you can rather than doing something like the above.
Here's an example, tested and working in Chrome, Firefox, IE11, IE8, and iOS Safari.(Live copy on JSBin):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Example</title>
<script>
(function() {
var d = document.createElement("div");
d.innerHTML =
"This is the element appended to the body element " +
"that we forced to be created using the evil " +
"<code>document.write</code>";
document.write("<body>");
document.body.appendChild(d);
})();
</script>
</head>
<body>
<div>
This is the first element inside the body element in the markup.
</div>
</body>
</html>
I tried putting my js directly in the head then my js can't use appendChild on body, because at that point document.body is null.
MutationObserver to the rescue!
You can simply wait for the <body> tag to be parsed:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script>
new MutationObserver(function(records, self)
{
for(var i = 0; i < records.length; ++i)
{
for(var j = 0; j < records[i].addedNodes.length; ++j)
{
if(records[i].addedNodes[j].nodeName == 'BODY')
{
self.disconnect();
console.log('herp');
/*
At this point, the body exists, but nothing inside it has been parsed yet.
document.body might be available, but to be safe, you can use:
var body = records[i].addedNodes[j];
*/
}
}
}
}).observe(document.documentElement,
{
childList: true,
});
</script>
</head>
<body>
<script>console.log('derp');</script>
</body>
</html>
Save this to an HTML file, open it in your browser, and you should see this in the console (indicating that the "herp" part runs before the "derp" one (note: Firefox seems to discard message order if the console is opened after the page loads, but the "herp" part is actually still running before the "derp" one)):
herp
derp
(Note: The above code won't work as a stack snippet, because everything is placed in the <body> tag there.)
Now just to be safe, I'd add a check to see if document.body is already set, and only set up the MutationObserver if that isn't the case:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script>
function onBodyLoaded(body)
{
console.log('herp');
/* Do whatever you want with "body" here. */
}
if(document.body)
{
onBodyLoaded(document.body)
}
else
{
new MutationObserver(function(records, self)
{
for(var i = 0; i < records.length; ++i)
{
for(var j = 0; j < records[i].addedNodes.length; ++j)
{
if(records[i].addedNodes[j].nodeName == 'BODY')
{
self.disconnect();
onBodyLoaded(records[i].addedNodes[j]);
}
}
}
}).observe(document.documentElement,
{
childList: true,
});
}
</script>
</head>
<body>
<script>console.log('derp');</script>
</body>
</html>
This way you might not have to add a <script> tag to your body at all, but just place the code you want to run there inside the onBodyLoaded function.
If you do need to add a script tag though, you can do so with:
function onBodyLoaded(body)
{
body.appendChild(document.createElement('script')).src = 'https://example.com/my.js';
}
or
function onBodyLoaded(body)
{
body.appendChild(document.createElement('script')).innerHTML = 'document.write("hi there")';
}
Note that IE 10 and earlier don't support MutationObserver. IE 11 and any other browser from this decade should work though.

How can I access the content of a dynamically created object tag in jquery?

I've been searching for an answer for this problem for several hours and I can't seem to find any solutions that work. I have created a dynamic object tag using jquery and set its data to another page on my website. I appended the object tag to a div and the div to the body of my page. What I want to do is access the html of the page that I set as the data of my object tag in order to monitor for changes on that page and update my current page based on those changes. When defining the object tag in html, I can access its contents using the contentDocument property of the object tag but when generating the tag dynamically, contentDocument is undefined. I've also tried using the contents function of jquery but it returns an object of length 0. Here is basically what I've tried:
This is the code for my page test.php:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
function test()
{
var parentDiv = document.createElement("div");
$(parentDiv).addClass("parentDiv");
var object = $("<object>");
$(object).attr("id","objectId");
$(parentDiv).append(object);
$("body").append(parentDiv);
$(object).attr("data","test2.html");
$(object).addClass("object");
// These give errors
var content = object.contentDocument;
alert(content.getElementById("div"));
var content = $(object).contents();
alert(content.length());
/* THIS WORKS IF object defined as a tag in HTML body
var content = document.getElementById("objectId").contentDocument;
alert(content.getElementById("div").innerHTML);
*/
}
</script>
</head>
<body onload="test()">
<!-- <object id="objectId" data="test2.html"></object> -->
</body>
</html>
And this is the code for the page of the object data test.html:
<!DOCTYPE html>
<html>
<head>
<title>Test 2</title>
</head>
<body>
<div id="div" style="width:200px;height:200px;background-color:red">
DIV Contents
</div>
</body>
</html>
Any help is greatly appreciated. Thank you.
At this moment, it is a jQuery object. So you need to define it:
// Replace it like this:
var content = document.getElementById("objectId").contentDocument;
var content = object.contentDocument;
alert(content.getElementById("div"));
var content = $(object).contents();
alert(content.length());
Or you can also use like:
// Replace it like this:
var content = $("#objectId").get(0).contentDocument;
var content = object.contentDocument;
alert(content.getElementById("div"));
var content = $(object).contents();
alert(content.length());
I think you have a number of HTML/Javascript concepts mixed up. I've corrected your code to at least have no errors:
var parentDiv = document.createElement("div");
$(parentDiv).addClass("parentDiv");
var object = document.createElement("object");
object.setAttribute("id", "objectId");
object.innerHTML = "Hello, Earth!";
$(parentDiv).append(object);
$("body").append(parentDiv);
object.setAttribute("data", "test2.html");
$(object).addClass("object");
var content = document.getElementById("objectId");
alert(content.innerHTML);
The jQuery '$' function, the same as the 'jQuery' function, which is an element selector, not an element creator. This code, var object = $("<object>") won't work because there does not exist an object on the page which is targeted by CSS selector <object>. I've changed that bit to document.createElement("object") instead.
As the object variable is now a DOM element, I've elected to use the .setAttribute function instead, which is a native DOM function.
$("body").append(parentDiv) will correctly add the parentDiv (and its child object) DOM element to the end of the body element. This allows $(object).addClass("object") to function properly.
The contentDocument property is exclusive to the iFrame element. I've changed the property to innerHTML instead. The alert box correctly displays "Hello, Earth!" which is the contents of the object element.
Working JSFiddle demo here.

Nested iframes, AKA Iframe Inception

Using jQuery I am trying to access div id="element".
<body>
<iframe id="uploads">
<iframe>
<div id="element">...</div>
</iframe>
</iframe>
</body>
All iframes are on the same domain with no www / non-www issues.
I have successfully selected elements within the first iframe but not the second nested iframe.
I have tried a few things, this is the most recent (and a pretty desperate attempt).
var iframe = jQuery('#upload').contents();
var iframeInner = jQuery(iframe).find('iframe').contents();
var iframeContent = jQuery(iframeInner).contents().find('#element');
// iframeContent is null
Edit:
To rule out a timing issue I used a click event and waited a while.
jQuery().click(function(){
var iframe = jQuery('#upload').contents().find('iframe');
console.log(iframe.find('#element')); // [] null
});
Any ideas?
Thanks.
Update:
I can select the second iframe like so...
var iframe = jQuery('#upload').contents().find('iframe');
The problem now seems to be that the src is empty as the iframe is generated with javascript.
So the iframe is selected but the content length is 0.
Thing is, the code you provided won't work because the <iframe> element has to have a "src" property, like:
<iframe id="uploads" src="http://domain/page.html"></iframe>
It's ok to use .contents() to get the content:
$('#uploads).contents() will give you access to the second iframe, but if that iframe is "INSIDE" the http://domain/page.html document the #uploads iframe loaded.
To test I'm right about this, I created 3 html files named main.html, iframe.html and noframe.html and then selected the div#element just fine with:
$('#uploads').contents().find('iframe').contents().find('#element');
There WILL be a delay in which the element will not be available since you need to wait for the iframe to load the resource. Also, all iframes have to be on the same domain.
Hope this helps ...
Here goes the html for the 3 files I used (replace the "src" attributes with your domain and url):
main.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>main.html example</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function () {
console.log( $('#uploads').contents().find('iframe').contents().find('#element') ); // nothing at first
setTimeout( function () {
console.log( $('#uploads').contents().find('iframe').contents().find('#element') ); // wait and you'll have it
}, 2000 );
});
</script>
</head>
<body>
<iframe id="uploads" src="http://192.168.1.70/test/iframe.html"></iframe>
</body>
iframe.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>iframe.html example</title>
</head>
<body>
<iframe src="http://192.168.1.70/test/noframe.html"></iframe>
</body>
noframe.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>noframe.html example</title>
</head>
<body>
<div id="element">some content</div>
</body>
var iframeInner = jQuery(iframe).find('iframe').contents();
var iframeContent = jQuery(iframeInner).contents().find('#element');
iframeInner contains elements from
<div id="element">other markup goes here</div>
and iframeContent will find for elements which are inside of
<div id="element">other markup goes here</div>
(find doesn't search on current element) that's why it is returning null.
Hey I got something that seems to be doing what you want a do. It involves some dirty copying but works. You can find the working code here
So here is the main html file :
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
Iframe = $('#frame1');
Iframe.on('load', function(){
IframeInner = Iframe.contents().find('iframe');
IframeInnerClone = IframeInner.clone();
IframeInnerClone.insertAfter($('#insertIframeAfter')).css({display:'none'});
IframeInnerClone.on('load', function(){
IframeContents = IframeInner.contents();
YourNestedEl = IframeContents.find('div');
$('<div>Yeepi! I can even insert stuff!</div>').insertAfter(YourNestedEl)
});
});
});
</script>
</head>
<body>
<div id="insertIframeAfter">Hello!!!!</div>
<iframe id="frame1" src="Test_Iframe.html">
</iframe>
</body>
</html>
As you can see, once the first Iframe is loaded, I get the second one and clone it. I then reinsert it in the dom, so I can get access to the onload event. Once this one is loaded, I retrieve the content from non-cloned one (must have loaded as well, since they use the same src). You can then do wathever you want with the content.
Here is the Test_Iframe.html file :
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>Test_Iframe</div>
<iframe src="Test_Iframe2.html">
</iframe>
</body>
</html>
and the Test_Iframe2.html file :
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>I am the second nested iframe</div>
</body>
</html>
You probably have a timing issue. Your document.ready commend is probably firing before the the second iFrame is loaded. You dont have enough info to help much further- but let us know if that seems like the possible issue.
You should use live method for elements which are rendered later, like colorbox, hidden fields or iframe
$(".inverter-value").live("change",function() {
elem = this
$.ajax({
url: '/main/invertor_attribute/',
type: 'POST',
aysnc: false,
data: {id: $(this).val() },
success: function(data){
// code
},
dataType: 'html'
});
});
I think the best way to reach your div:
var your_element=$('iframe#uploads').children('iframe').children('div#element');
It should work well.
If browser supports iframe, then DOM inside iframe come from src attribute of respective tag. Contents that are inside iframe tag are used as a fall back mechanism where browser does not supports iframe tag.
Ref: http://www.w3schools.com/tags/tag_iframe.asp
I guess your problem is that jQuery is not loaded in your iframes.
The safest approach is to rely on pure DOM-based methods to parse your content.
Or else, start with jQuery, and then once inside your iframes, test once if typeof window.jQuery == 'undefined', if it's true, jQuery is not enabled inside it and fallback on DOM-based method.

Write elements into a child iframe using Javascript or jQuery

I have something like this:
<!doctype html>
<html>
<body>
<iframe id="someFrame"></iframe>
</body>
</html>
And I would like to use jQuery to write elements such that the full equivalent HTML would be like this:
<!doctype html>
<html>
<body>
<iframe id="someFrame">
<!-- inside the iframe's content -->
<!-- <html><body> -->
<div>A</div>
<div>B</div>
<div>C</div>
<!-- </body></html> -->
</iframe>
</body>
</html>
Alternatively, any plain-old-Javascript would be fine.
Thanks.
Edit: After a little more research, it seems I am looking for an IE-equivalent of the contentDocument property of an iframe. "contentDocument" is a W3C standard which FF supports, but IE does not. (surprise surprise)
You can do both, you just have to target differently:
var ifrm = document.getElementById('myIframe');
ifrm = ifrm.contentWindow || ifrm.contentDocument.document || ifrm.contentDocument;
ifrm.document.open();
ifrm.document.write('Hello World!');
ifrm.document.close();
After some research, and a corroborating answer from Mike, I've found this is a solution:
var d = $("#someFrame")[0].contentWindow.document; // contentWindow works in IE7 and FF
d.open(); d.close(); // must open and close document object to start using it!
// now start doing normal jQuery:
$("body", d).append("<div>A</div><div>B</div><div>C</div>");
There are two reliable methods to access the document element inside an iframe:
1. The window.frames property:
var iframeDocument = window.frames['iframeName'].document; // or // var iframeDocument = window.frames[iframeIndex].document;
Demo
2. The contentDocument property:
var iframeDocument = document.getElementById('iframeID').contentDocument; // or // var iframeDocument = document.getElementById('iframeID').contentWindow.document;
Demo
I am going out on a limb here and suggest that the answers proposed so far are not possible.
If this iframe actually has a src="somepage.html" (which you ought to have indicated, and if not, what is the point of using iframe?), then I do not think Jquery can directly manipulate html across frames in all browsers. Based on my experience with this kind of thing, the containing page cannot directly call functions from or make any sort of Javascript contact with the iframe page.
Your "somepage.html" (the page that loads in the iframe) needs to do two things:
Pass some kind of object to the containing page that can be used as a bridge
Have a function to set the HTML as you desired
So for example, somepage.html might look like this:
<!doctype html>
<html>
<head>
<script src="jquery.js">
</script>
<script language=JavaScript>
<!--//
var bridge={
setHtml:function(htm) {
document.body.innerHTML=htm;
}
}
$(function() { parent.setBridge(bridge); });
//--></script>
</head>
<body></body>
</html>
and the containing page might look like this:
<!doctype html>
<html>
<head>
<script src="jquery.js">
</script>
<script language=JavaScript>
<!--//
var bridge;
var setBridge=function(br) {
bridge=br;
bridge.setHtml("<div>A</div><div>B</div><div>C</div>");
}
//-->
</script>
</head>
<body><iframe src="somepage.html"></iframe></body>
</html>
This may appear a bit convoluted but it can be adapted in a number of directions and should work in at least IE, FF, Chrome, and probably Safari and Opera...
I have found this to be cross-browser compatible... a little crossing of previous answers and a bit of trial & error of my own. :)
I'm using this for a download of a report, or, if an error (message) occurs, it's displayed in the iFrame. Most of the users will probably have the iFrame hidden, I'm using it multi-functional.
The thing is I have to clear the contents of the iFrame every time I click the report download button - the user can change parameters and it happens there are no results which then is displayed in the iFrame as a message. If there are results, the iFrame remains empty - because the code below has cleared it and the window.open(...) method generates a Content-Disposition: attachment;filename=... document.
var $frm = $("#reportIFrame");
var $doc = $frm[0].contentWindow ? $frm[0].contentWindow.document : $frm[0].contentDocument;
var $body = $($doc.body);
$body.html(''); // clear iFrame contents <- I'm using this...
$body.append('<i>Writing into the iFrame...</i>'); // use this to write something into the iFrame
window.open(Module.PATH + 'php/getReport.php' + Report.queryData, 'reportIFrame');
I do not have a browser that supports contentDocument but I've coded it this way so I'm leaving it. Maybe someone has older browsers and can post compatibility confirmation/issues?

Categories

Resources