I have small snippet that is inside iframe and generates script html tag and appends it to the window.top.document.head.
Now I want to know how do I check from within potato.js from which iframe it was generated from once it is already loaded?
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<iframe>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script>
(function() {
var s = document.createElement('script');
s.setAttribute('type','text/javascript');
s.setAttribute('src','https://test.com/potato.js');
window.top.document.head.appendChild(s);
})();
</script>
</body>
</html>
</iframe>
</body>
</html>
Edit: I can not change this code inside the iframe
That information isn't stored automatically.
The only way I can think of would be to add an expando-prop to the script with a reference to the current window (i.e. the frame's window)…
s.sourceWindow = window;
… then read that from within potato.js …
const sourceWindow = document.currentScript.sourceWindow;
… and then loop over all the frames (window.frames) looking for a match.
Since you are using window.top and not window.parent you might need to be recursive there.
Background
I am attempting to read (and eventually change) content within the body of an IFrame (in my example this involves h1 tags) embedded in a Confluence page.
Confluence Behavior
Initially I didn't think it was significant, but as comments by #ScottMarcus illustrate; it does matter that this work is for a possible Confluence plugin. This is because I am using a page decorator containing JavaScript that executes when a user edits a Confluence page.
IFrame Restrictions
This means that (to my knowledge) I cannot have JavaScript execute after the editable IFrame has been added to the DOM. It also means that I cannot control what content is added to the IFrame (although this is OK/expected, as the idea is to enhance the user experience if they add tables and install the plugin).
Example
For reference, here is what the wysiwyg Confluence editor looks like for my page (annotated with a couple key elements from my HTML below):
Problem
Although I have been able to read/log the HTML present within the IFrame, I cannot seem to access elements in the same way I am able to within the main document. I have tried different variations on what I have below, doing things like...
selecting elements by ID--for some reason this seems less reliable in this context?
trying to get the contents() or children() of elements--in many cases I get null reference exceptions when trying this
accessing the body from my IFrame object--again, this doesn't seem the same as doing document.body
JavaScript
function doStuff() {
$('#wysiwygTextarea_ifr').ready(function () {
let iFrame = document.getElementById('wysiwygTextarea_ifr');
let frameDoc = iFrame.contentDocument ? iFrame.contentDocument : iFrame.contentWindow.document;
let h1Tags = frameDoc.getElementsByTagName('h1');
//would like to iterate over the collection of h1 tags here, but it always seems to be empty
});
}
HTML
<!DOCTYPE html>
<html>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="../Libraries/main.js" type="text/javascript"></script>
<script type="text/javascript">
doStuff();
</script>
<iframe id="wysiwygTextarea_ifr">
#document
<!DOCTYPE html>
<html>
<head>
<title>blah</title>
</head>
<body id="tinymce">
<h1 id="meh">abcde table</h1>
<h1 id="neh">zzz</h1>
</body>
</html>
</iframe>
</html>
I am using embed tag to load pdf. It's a legacy application so can't use other approach. I want to run some javascript function when pdf loads completely in embed tag. How can I do this?
I have tried using onload event on embed tag but it doesn't work.
<!doctype html>
<html>
<head>
<title></title>
<script type="text/javascript">
//This function should be called when pdf loads.
function ContentLoaded()
{
//Some code which should execute only after PDF loads on browser
}
</script>
</head>
<body>
<embed id="content" src="myfile.pdf"/>
</body>
</html>
I suggest you to place <script> tag just before the closing body tag (</body>). This way, JS code will run after the HTML content loaded.
If you place the code below before the closing head tag (</head>), it shows you error in developer's console. But if you place it before </body> tag, it works just fine.
<script type="text/javascript">
var pdf = document.getElementById('content');
console.log('pdf:', pdf);
var w = pdf.getAttribute('width');
console.log('pdf width is exactly:', w, 'pixels');
</script>
With this minimal webpage:
<html>
<head>
<title>test</title>
<script type='text/javascript' src='test.js'></script>
</head>
<body>
</body>
</html>
This minmal 'test.js' script fails:
document.body.innerText = 'testje';
The document.body seems to be undefined:
0x800a138f - JavaScript runtime error: Unable to set property 'innerText'
of undefined or null reference
If one adds a document.write('blabla') before the code line, it works:
document.write('blabla');
document.body.innerText = 'testje';
This puzzles me, although it seems rather basic.
I am a Javascript newbe, so some explanation would be most welcome.
At the time the script is running there is no body tag, it hasn't been outputted to the DOM yet.
<html>
<head>
<title>test</title>
</head>
<body>
<!-- Place content here -->
<script type='text/javascript' src='test.js'></script>
</body>
</html>
when using document.write, the document is synchronously updated with the new content, and the browser fixes your mistake and adds a body element, that's why it's available after document.write.
The script appears in the head. At the time it runs, the start of the body has not been encountered, so the body does not exist and cannot be accessed via the DOM.
document.write writes HTML to the document. In this case, character data is being written. Since character data is not allowed in the head and the end tag for head and the start tag for body are optional, this ends the head element and starts the body. At this point the body exists and can be accessed.
You can't access body element until it's declared and your script is running in <head>.
Try this: window.onload = function(){ document.body.innerText = 'testje'; };
or place your script below <body>
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.