Determine when iFrame content is rendered after IFrame has already loaded - javascript

Using javascript I'm creating an iframe, adding it to the parent DOM and then writing HTML to it via iFrameEl.contentWindow.document.write()
I need to determine when all content in the iFrame has been rendered after iFrameEl.contentWindow.document.closed() has been called.
The iFrame's onload event appears to be called when the iframe is added to the DOM originally, before I can write to it and I need to add it to the DOM in order to get access to the iFrame's contentWindow.document, so catch 22.
In Chrome, as an alternative I have used the srcdoc attribute to add all the content I need to the iframe and the add the iframe to the dom and wait for the onload event however srcdoc is not supported in IE
var iFrameHTML = '<!DOCTYPE html><html><head>';
iFrameHTML = iFrameHTML + '</head><body>' + htmlIncludingImagesEtc + '</body></html>';
iFrameEl.srcdoc = iFrameHTML;
myDiv.append(iFrame);
iFrameEl.onload = function(){
//proceed
}
I know the iFrame's src attribute can also be used, but the limitation on Data URI characters prevents me from using that approach
I know there are many similar questions on here, but I could not find any that matches my requirements.
I need to dynamically create an iFrame, add content to it and determine when all the content has been rendered, is there some obvious way to achieve this, or will it require MutationObservers etc.?

Check out DOMFrameContentLoaded. It is an event that will be fired when the content in the frame is loaded and parsed.

Late answer, but it may help someone...I found that even after the iframe was added to the DOM, if I added the links to style sheets from the head of the parent DOM to the iframe as a String as follows, it meant that the load event occurred
iFrame$.on('load',function(){
//iFrame loaded
});
var headHTML = '<head>';
$('head link').each(function(index, link) {
headHTML = headHTML + link.outerHTML;
});
headHTML = headHTML + '</head>';
iFrameDoc.open();
iFrameDoc.write("<!DOCTYPE html>");
iFrameDoc.write("<html>");
iFrameDoc.write(headHTML);
iFrameDoc.write("</body>");
iFrameDoc.write("</html>");
iFrameDoc.close();

Related

HTML object is blocking eventlistener

I have imported a svg as an object in HTML:
<object data="mySVG.svg" type="image/svg+xml" id="circle">
<img src="mySVG.svg" />
</object>
and I am trying to set an eventlistener on the whole page:
window.addEventListener('click', function(){
alert('Hello')
})
The problem is that the object blocks the eventlistener and when the user clicks on the image the alert is not fired. But when the user clicks anywhere else or over other elements, the alert is fired. How can I make it so the object is acting as the other elements and doesn't block the eventlistener?
I tried wait after the object is beaing loaded and then set the eventlistener but it didn't work.
If I import the SVG directly into HTML with svg tag it works, but the svg is quit big and it makes the HTML code really messy. I can't use the img tag either becuase I am also interacting with parts of the SVG with JS later.
As it can be seen in this codepen I've made: https://codepen.io/Dimertuper/pen/rNJoLrK (When you click outside the image it triggers, inside the image it doesn't)
Your <object> acts like an <iframe>, just like we wouldn't want any website to be able to embed our bank website in an iframe and see where we clicked, the <object> has the same "protection".
Even if the page are same-origin and can talk to each other, by default they won't receive any events from the other one.
But anyway what you probably want is to make the SVG document react to these events. For this, add the event listeners on that document directly.
// Wait for the <object> to be loaded
window.addEventListener("load", (evt) => {
const objEl = document.querySelector("object");
const svgDoc = objEl.getSVGDocument();
// Now you have access to the SVG document
// you can add event listeners to it as you wish
svgDoc.addEventListener("click", (evt) => {
console.log("clicked on", evt.target.outerHTML);
});
});
Unfortunately StackSnippets's null-origined iframes won't allow us to make live demos, so here is one on JSFiddle.
But beware the <object> element isn't gathering much love from implementers and spec authors these days and it may get removed from the standards at some point in the future.
So instead, you may prefer to actually use an <iframe> directly. Moreover since here we would access the loaded document, we can do the one thing that <object> can do and <iframe> can't: auto-resizing to the image content.
For this, when we get our SVG document, we grab its documentElement's BBox and set our <iframe>'s width and height attributes to the BBox's ones.
// Wait for the <iframe> to be loaded
window.addEventListener("load", (evt) => {
const frameEl = document.querySelector("iframe");
const svgDoc = frameEl.getSVGDocument();
// Resize the iframe to its content's size
const bbox = svgDoc.documentElement.getBBox();
frameEl.width = bbox.width;
frameEl.height = bbox.height;
svgDoc.addEventListener("click", (evt) => {
console.log("clicked on", evt.target.outerHTML);
});
});
Once again as a JSFiddle.
Per OP's requirements -
Needs to be able to click on window/document and receive the alert message even when clicking on the HTML object tag.
We can do this by removing the object tag as a clickable element with CSS pointer-events: none;.
object {
pointer-events: none;
}
https://codepen.io/LTFoReal/pen/NWyerZg?editors=1111
This link has work around. Using a transparent div to cover object image, or directly use svg image instead.
I checked the specification of object element. It's for embeded external content usage. So it has ability to load a full document, your case is load as image. The available property to do event binding for this element is contentDocument or getSvgDocument(). Both are null under your case, as it's loaded as svg image.
document.getElementsByTagName("object")[0].contentDocument
Check this link for detail. Hope this helps you.

Injecting html dom inside iframe's document and/or shadow-root

I am injecting iframe onto the webpage using javascript (using chrome extension). Using javascript, I am creating iframe and I can access my html's url, so I though I can fetch the DOM using $.get
var elt = document.createElement('iframe');
elt.id = 'my_iframe';
$.get(chrome.extension.getURL('views/test.html'), function(doc) {
var container = document.querySelector('#my_iframe');
console.log(container.documentContent); // can access
console.log(container.shadowRoot); // can't access
// ideally:
container.shadowRoot($(doc));
container.contentDocument($(doc));
});
document.getElementsByTagName('body')[0].appendChild(elt);
How can I inject my 'text.html' inside the #shadow-root?
The reason is that, when I use elt.src = url, it doesn't wrap the html content inside #document or #shadow-root, and this results in a bug - (blurry content in iframe when not wrapped in #document or #shadow-root - bug report)
If you are just changing the contents of an iframe, you can do it by:
const iframe = document.querySelector('#myIframe')
iframe.outerHTML = '<iframe></iframe>' // clears the iframe
iframe.contentDocument.open()
iframe.contentDocument.write(yourHTMLText)
iframe.contentDocument.close()

Javascript Iframe not getting height calculated correctly

I am trying to display my iframe on any website like this one HERE but for some reason the height is not calculated correctly. I get 8px on the style tag instead of 1000+ to display correctly that block.
<iframe id="iframe" allowtransparency="true" frameborder="0" style="width:100%; border:none" scrolling="no" src="http://newskillsacademy.co.uk/affiliates/iframe.php?&products_ids[]=55072&products_ids[]=51883&products_ids[]=49321&products_ids[]=48561&products_ids[]=48398&products_ids[]=46469&products_ids[]=44080&products_ids[]=43167&products_ids[]=42427&products_ids[]=41068&columns=3&aff_id=3"></iframe>
<script>
var frame = document.getElementById('iframe');
frame.style.height = 0;
frame.style.height = frame.contentWindow.document.body.scrollHeight + 'px';
</script>
The line that sets the height (i.e. frame.style.height = frame.contentWindow.document.body.scrollHeight + 'px';) is likely run before the contents of the iframe have loaded. Thus in order to set the height properly, that code must be executed after the contents of the iframe have loaded.
One way to do this is to assign the onload property to a function containing the code to set the height property:
var frame = document.getElementById('iframe');
frame.onload = function() {
frame.style.height = frame.contentWindow.document.body.scrollHeight + 'px';
}
See this demonstrated here.
Also, the code attempts to fetch the iframe from the DOM as soon as the javascript executes. Depending on the location of the script tag/file (e.g. loaded via <head> or <body>) the DOM may not be ready. One solution to this is waiting for the DOMContentLoaded event. This can be achieved using EventTarget.addEventListener() on document.
document.addEventListener("DOMContentLoaded", function(event) {
console.log("DOM fully loaded and parsed");
});
And because you initially tagged the question with jQuery, the .ready() method provides a shortcut to this.
$(document).ready(function(event) {
console.log("DOM is safe to manipulate");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Removing iframe from page

I am creating an iframe dynamically for submmiting a form,after submitting i need to remove the iframe form the page.I removed itas follows but it is not removed,
function remove(){
var frame = document.getElementById("upload_iframe"),
var frameDoc = frame.contentDocument || frame.contentWindow.document;
frameDoc.removeChild(frameDoc.documentElement);
}
How to remove the ifarme form the form completely.
Thanks
Frame has 2 behaviors: frame as document element (like div or another DOM element) and frame as window element (like global window object). So if you want to remove iframe from DOM tree you have to work with iframe like with DOM element
function remove(){
var frame = document.getElementById("upload_iframe");
frame.parentNode.removeChild(frame);
}
A way I've been doing it (since I have a large amount of iframes) is using jQuery,
$('iframe').remove()
or in the case of only one iframe you can remove it using its ID, still with jQuery
$('#iframeID').remove()

Reload iframe src / location with new url not working in Safari

I have a page that loads with initially just a form within an iframe, something like this:
<iframe id="oIframe" ...src='somePage>'
<form ... />
</iframe>
When you click a button in the form, some javascript is invoked that builds a url and then I want to do the following:
frame.src = 'somePage?listId=1';
This works in IE to "reload" the frame with the new contents.
However, in Safari this does not work.
I have jQuery available, but I don't want to replace the existing iframe because there are events attached to it. I also can not modify the id of the iframe because it is referenced throughout the application.
I have seen some similar issues but no solutions that seem to work well for my exact issue.
Any assistance anyone can provide would be great!
Some browsers don't use "src" when calling the javascript object directly from the javascript hierarchy and others use "location" or "href" instead of "src" to change the url . You should try these two methods to update your iframe with a new url.
To prevent browser cache add a pseudo-random string like a number and timestamp to the url to prevent caching. For example add "new Date().getTime()" to your url.
Some calling examples:
document.getElementById(iframeId).src = url;
or
window.frames[iframeName].location = url;
I recommend the first option using document.getElementById
Also you can force the iframe to reload a page using
document.getElementById(iframeId).reload(true);
So the answer is very simple:
1. put a <div id="container"> </div> on your page
2. when reload needed use following jQuery:
$("#container").empty();
$("#container").append("<iframe src='" + url + "' />");
and that's it.
Of course there is more elegant way of creating DOM with jQuery but this gives the idea of "refreshing" iframe.
Works in FF18, CH24, IE9, O12 (well it's jQuery so it will work almost always :)
I found a better solution (albeit not paticularly eloquent) for this using jQuery.ajax:
jQuery.ajax({
type: 'GET',
url: "/somePage?someparms",
success: function() {
frameObj.src = "/somePage?someparms";
}
});
This forces the DOM to be read within the frame object, and reloads it once the server is ready to respond.
Try this
form.setAttribute('src', 'somePage?listId=1');
Well, I was able to find what appears to be a feasible solution -- it's a work in progress, but this is basically what I ended up doing:
var myFrame = document.getElementById('frame'); // get frame
myFrame.src = url; // set src attribute of original frame
var originalId = myFrame.id; // retain the original id of the frame
var newFrameId = myFrame.id + new Date().getTime(); // create a new id
var newFrame = "<iframe id=\"" + newFrameId + "\"/>"; // iframe string w/ new id
myFrameParent = myFrame.parentElement; // find parent of original iframe
myFrameParent.innerHTML = newFrame; // update innerHTML of parent
document.getElementById(newFrameId).id = originalId; // change id back
I ran into this issue using React, passing the key as props.src solved it
const KeyedIframe = ({children, ...props}) => <iframe key={props.src} { ...props}>
{children}
</iframe>

Categories