I have a function that loads a SVG Dom from a file. Currently, it creates an embed element and places it in the document, then waits for it to load with an onload event. Apparently, however, onload isn't called for elements placed in the document after the page has loaded. Is there a way that I can register a function to be called after the element has finished loading?
This is what I have:
function loadSVG(svgFilename, callback)
{
// Loads data from an svg file
// loadSVG(
// svgFilename, // The path to the file to load
// callback // The function to be called with the
// // SVG document once it loads.
// );
var embed = document.createElement("embed");
embed.src = svgFilename;
embed.onload = function() // Doesn't get called because page has already loaded
{
var doc = embed.getSVGDocument();
document.getElementById("SVGLoader").removeChild(embed);
callback(doc);
};
document.getElementById("SVGLoader").appendChild(embed);
}
I figured out the problem, I was loading the SVG document in a div tag that was hidden with style="display:none". For some reason, the browser didn't load the embed when it was in this tag. When I removed the style attribute, the onload event fired the way I expected it to.
Relevant: How to check if an embedded SVG document is loaded in an html page?
If you have control over the SVG document, could you not add a script in it that calls a function in the browser window?
Or, as the answer to that question suggested, polling to see if the SVG document has finished loading.
Related
I need the same source i can find in the Elements window of DevTool console in my extension. I tried using the content script
var text = document.documentElement.innerHTML;
injected after catched the "complete" status from chrome.tabs.onUpdated.addListener, but i recived only the html code without the content dynamically created.
In particular i want my extension to find all "div" added dynamically.
Any help will be appreciated!
The complete event fires once the initial page content has been loaded. It has no relation to dynamically generated content, otherwise it would have to wait indefinitely, since more content may always be added later.
If you are interested in a specific element, you can use setTimeout to periodically poll for the element. Like so:
function getElement() {
return new Promise(function(res, rej) {
var interval = setInterval(function() {
var elm = document.getElementById('the-element-you-want');
if(elm){
clearInterval(interval);
res(elm);
}
}, 10);
});
}
Another option would be to use a MutationObserver to detect when the desired element(s) have been created.
I know the .load() function in jQuery, and how to use the callback function with that... but is there a way to check if an element has been loaded using an if statement?
My reasoning... I want to set an interval and check if an element and all it's children have been fully loaded.
var load-interval = setInterval(function(){
if($('#content').hasBeenLoaded){
//do stuff
}
}, 1000);
var $content = $('#content');
var intervalId = setInterval(function(){
if(!$content.is(':empty')){
//do stuff
clearInterval(intervalId);
}
}, 1000);
Just note that elements that contains only text nodes considered to be non empty by the spec:
During initial page load, your DOM elements are progressively created as the file is parsed, but image assets are typically loaded asynchronously as they're referenced.
So, as your page loads, the parser will come across the element <div id="mycontent">.
This element will be created immediately, and then its children, and then eventually the closing tag for that div will be found. At that point you could consider that the DOM itself is "loaded" for that element.
The simplest way to execute something then is to put it in a <script> tag immediately after that closing tag.
If you also want to wait for the image assets to load, then this is still the place to put it. You can look for all <img> tags within the previously loaded div, and register onload callbacks, e.g.
<div id="mycontent">
lots of DOM, including image tags
</div>
<script>
(function() {
var div = document.getElementById('mycontent'); // guaranteed to exist
var imgs = div.getElementsByTagName('img');
// put image load detection code here - exercise for the reader
})();
</script>
I am creating a Firefox extension. I want load my external js file for all web pages on load time using Firefox extension. I can successfully load all js file on load event in my html file. But how can i execute into my extension.
window.onload = load;
function load()
{
var filerefs=document.createElement('script')
filerefs.setAttribute("type","text/javascript")
filerefs.setAttribute("src", 'http://code.jquery.com/jquery-1.9.1.min.js')
var head = doc.getElementsByTagName( "head" )[ 0 ].appendChild( filerefs );
}
Please guide me.
If you want to detect page load and not application load, you should do it like this (this first part should be in your main overlay file):
// do not try to add a callback until the browser window has
// been initialised. We add a callback to the tabbed browser
// when the browser's window gets loaded.
window.addEventListener("load", function (event) {
// Add a callback to be run every time a document loads.
// note that this includes frames/iframes within the document
gBrowser.addEventListener("load", load(event), true);
}, false);
Reference from here
I added the event to the load function because the event fires not only on the load itself, but also on other elements, so you have to validate that you are doing it only once (reference here ):
function load(event) {
if (event.originalTarget.nodeName == "#document") {
var htmlns = "http://www.w3.org/1999/xhtml";
var doc = gBrowser.selectedBrowser.contentDocument;
var filerefs = doc.createElementNS(htmlns,'script');
filerefs.setAttribute("type","text/javascript");
filerefs.setAttribute("src", "http://code.jquery.com/jquery-1.9.1.min.js");
doc.getElementsByTagName("head")[0].appendChild(filerefs);
}
}
Instead of using document.getElement..., we have to position on the content of the page, otherwise you are getting elements from your addon (using gBrowser.selectedBrowser.contentDocument gives you access to the current page's dom). And on the createlement, we want to create html elements, so you define and use the html namespace (reference here ).
This should do the trick:
I need to execute a callback when an IFRAME has finished loading. I have no control over the content in the IFRAME, so I can't fire the callback from there.
This IFRAME is programmaticly created, and I need to pass its data as a variable in the callback, as well as destroy the iframe.
Any ideas?
EDIT:
Here is what I have now:
function xssRequest(url, callback)
{
var iFrameObj = document.createElement('IFRAME');
iFrameObj.src = url;
document.body.appendChild(iFrameObj);
$(iFrameObj).load(function()
{
document.body.removeChild(iFrameObj);
callback(iFrameObj.innerHTML);
});
}
This callsback before the iFrame has loaded, so the callback has no data returned.
First up, going by the function name xssRequest it sounds like you're trying cross site request - which if that's right, you're not going to be able to read the contents of the iframe.
On the other hand, if the iframe's URL is on your domain you can access the body, but I've found that if I use a timeout to remove the iframe the callback works fine:
// possibly excessive use of jQuery - but I've got a live working example in production
$('#myUniqueID').load(function () {
if (typeof callback == 'function') {
callback($('body', this.contentWindow.document).html());
}
setTimeout(function () {$('#frameId').remove();}, 50);
});
I am using jQuery and surprisingly this seems to load as I just tested and loaded a heavy page and I didn't get the alert for a few seconds until I saw the iframe load:
$('#the_iframe').load(function(){
alert('loaded!');
});
So if you don't want to use jQuery take a look at their source code and see if this function behaves differently with iframe DOM elements, I will look at it myself later as I am interested and post here. Also I only tested in the latest chrome.
I have had to do this in cases where documents such as word docs and pdfs were being streamed to the iframe and found a solution that works pretty well. The key is handling the onreadystatechanged event on the iframe.
Lets say the name of your frame is "myIframe". First somewhere in your code startup (I do it inline any where after the iframe) add something like this to register the event handler:
document.getElementById('myIframe').onreadystatechange = MyIframeReadyStateChanged;
I was not able to use an onreadystatechage attribute on the iframe, I can't remember why, but the app had to work in IE 7 and Safari 3, so that may of been a factor.
Here is an example of a how to get the complete state:
function MyIframeReadyStateChanged()
{
if(document.getElementById('myIframe').readyState == 'complete')
{
// Do your complete stuff here.
}
}
The innerHTML of your iframe is blank because your iframe tag doesn't surround any content in the parent document. In order to get the content from the page referred to by the iframe's src attribute, you need to access the iframe's contentDocument property. An exception will be thrown if the src is from a different domain though. This is a security feature that prevents you from executing arbitrary JavaScript on someone else's page, which would create a cross-site scripting vulnerability. Here is some example code the illustrates what I'm talking about:
<script src="http://prototypejs.org/assets/2009/8/31/prototype.js" type="text/javascript"></script>
<h1>Parent</h1>
<script type="text/javascript">
function on_load(iframe) {
try {
// Displays the first 50 chars in the innerHTML of the
// body of the page that the iframe is showing.
// EDIT 2012-04-17: for wider support, fallback to contentWindow.document
var doc = iframe.contentDocument || iframe.contentWindow.document;
alert(doc.body.innerHTML.substring(0, 50));
} catch (e) {
// This can happen if the src of the iframe is
// on another domain
alert('exception: ' + e);
}
}
</script>
<iframe id="child" src="iframe_content.html" onload="on_load(this)"></iframe>
To further the example, try using this as the content of the iframe:
<h1>Child</h1>
Google
<p>Use the preceeding link to change the src of the iframe
to see what happens when the src domain is different from
that of the parent page</p>
I wanted to hide the waiting spinner div when the i frame content is fully loaded on IE, i tried literally every solution mentioned in Stackoverflow.Com, but with nothing worked as i wanted.
Then i had an idea, that when the i frame content is fully loaded, the $(Window ) load event might be fired. And that exactly what happened. So, i wrote this small script, and worked like magic:
$(window).load(function () {
//alert("Done window ready ");
var lblWait = document.getElementById("lblWait");
if (lblWait != null ) {
lblWait.style.visibility = "false";
document.getElementById("divWait").style.display = "none";
}
});
Hope this helps.
This function will run your callback function immediately if the iFrame is already loaded or wait until the iFrame is completely loaded.
This also addresses the following issues:
Chrome initializes every iFrame with an about:blank page which will have readyState == "complete". Later, it will replace `about:blank with the actual iframe src value. So, the initial value of readyState will not represent the readyState of your actual iFrame. Therefore, besides checking for readyState value, this function also addresses the about:blank issue.
DOMContentLoaded event doesn't work with iFrame. So it uses the load event for running the callback function if iFrame isn't already loaded. The load event is equivalent to readyState == "complete" which has been used to check whether iFrame is already loaded. So, in any scenario, the callback function will run after iFrame is fully loaded.
iFrame src can have redirects and therefore load a page different from the original src url. This function will also work in that scenario.
Pass in your callback function that you want to run when the iFrame finishes loading and the <iframe> element to this function:
function iframeReady(callback, iframeElement) {
const iframeWindow = iframeElement.contentWindow;
if ((iframeElement.src == "about:blank" || (iframeElement.src != "about:blank" && iframeWindow.location.href != "about:blank")) && iframeWindow.document.readyState == "complete") {
callback();
} else {
iframeWindow.addEventListener("load", callback);
}
}
I had a similar problem as you. What I did is that I use something called jQuery. What you then do in the javascript code is this:
$(function(){ //this is regular jQuery code. It waits for the dom to load fully the first time you open the page.
$("#myIframeId").load(function(){
callback($("#myIframeId").html());
$("#myIframeId").remove();
});
});
It seems as you delete you iFrame before you grab the html from it. Now, I do see a problem with that :p
Hope this helps :).
I have a similar code in my projects that works fine.
Adapting my code to your function, a solution could be the following:
function xssRequest(url, callback)
{
var iFrameObj = document.createElement('IFRAME');
iFrameObj.id = 'myUniqueID';
document.body.appendChild(iFrameObj);
iFrameObj.src = url;
$(iFrameObj).load(function()
{
callback(window['myUniqueID'].document.body.innerHTML);
document.body.removeChild(iFrameObj);
});
}
Maybe you have an empty innerHTML because (one or both causes):
1. you should use it against the body element
2. you have removed the iframe from the your page DOM
I think the load event is right.
What is not right is the way you use to retreive the content from iframe content dom.
What you need is the html of the page loaded in the iframe not the html of the iframe object.
What you have to do is to access the content document with iFrameObj.contentDocument.
This returns the dom of the page loaded inside the iframe, if it is on the same domain of the current page.
I would retreive the content before removing the iframe.
I've tested in firefox and opera.
Then i think you can retreive your data with $(childDom).html() or $(childDom).find('some selector') ...
I've had exactly the same problem in the past and the only way I found to fix it was to add the callback into the iframe page. Of course that only works when you have control over the iframe content.
Using onload attrbute will solve your problem.
Here is an example.
function a() {
alert("Your iframe has been loaded");
}
<iframe src="https://stackoverflow.com" onload="a()"></iframe>
Is this what you want?
Click here for more information.
I need to edit (using javascript) an SVG document embedded in an html page.
When the SVG is loaded, I can access the dom of the SVG and its elements. But I am not able to know if the SVG dom is ready or not, so I cant' perform default actions on the SVG when the html page is loaded.
To access the SVG dom, I use this code:
var svg = document.getElementById("chart").getSVGDocument();
where "chart" is the id of the embed element.
If I try to access the SVG when the html document is ready, in this way:
jQuery(document).ready( function() {
var svg = document.getElementById("chart").getSVGDocument();
...
svg is always null. I just need to know when it is not null, so I can start manipulate it.
Do you know if there is a way to do it?
On your embedding element (e.g 'embed', 'object', 'iframe') in the main document add an onload attribute which calls your function, or add the event listener in script, e.g embeddingElm.addEventListener('load', callbackFunction, false). Another option might be to listen for DOMContentLoaded, depends on what you want it for.
You can also add a load listener on the main document. jQuery(document).ready doesn't mean that all resources are loaded, just that the document itself has a DOM that is ready for action. However note that if you listen for load on the entire document your callback function won't be called until all resources in that document are loaded, css, javascript etc.
If you use inline svg, then jQuery(document).ready will work just fine however.
On a further note you might want to consider using embeddingElm.contentDocument (if available) instead of embeddingElm.getSVGDocument().
You could use an onload event for the check.
Suppose some.svg is embedded in object tag :
<body>
<object id="svgholder" data="some.svg" type="image/svg+xml"></object>
</body>
Jquery
var svgholder = $('body').find("object#svgholder");
svgholder.load("image/svg+xml", function() {
alert("some svg loaded");
});
javascript
var svgholder = document.getElementById("svgholder");
svgholder.onload = function() {
alert("some svg loaded");
}
Assuming your SVG is in an <embed> tag:
<embed id="embedded-image" src="image.svg" type="image/svg+xml" />
The SVG image is essentially in a sub-document that will have a separate load event to that of the main document. However, you can listen for this event and handle it:
var embed = document.getElementById("embedded-image");
embed.addEventListener('load', function()
{
var svg = embed.getSVGDocument();
// Operate upon the SVG DOM here
});
This is better than polling as any modification you make to the SVG will happen before it is first painted, reducing flicker and CPU effort spent painting.
The load event of the embedding element (e.g. object) would be my preference but, if that isn't a viable solution for some reason, the only generic and reliable test I've found for SVG DOM ready is the getCurrentTime method of the SVG root element:
var element = document.getElementById( 'elementId' );
var svgDoc = element.contentDocument;
var svgRoot = svgDoc ? svgDoc.rootElement : null;
if ( svgRoot
&& svgRoot.getCurrentTime
&& ( svgRoot.getCurrentTime() > 0 ))
{
/* SVG DOM ready */
}
The W3C SVG recommendation states that getCurrentTime on an SVGSVGElement:
Returns the current time in seconds relative to the start time for the current SVG document fragment. If getCurrentTime is called before the document timeline has begun (for example, by script running in a ‘script’ element before the document's SVGLoad event is dispatched), then 0 is returned.
Using jQuery you can bind to the window load event Erik mentions with:
$(window).load(function(){
var svg = document.getElementById("chart").getSVGDocument();
});
For future reference: an Angular(8)/Typescript solution looks like this:
#ViewChild('startimage', {static:false})
private startimage: ElementRef;
...
this.startimage.nativeElement.addEventListener('load', () => {
alert('loaded');
});
You can get to the svg by const svg = this.startimage.nativeElement.getSVGDocument();
You can assign an onload event handler to an element within your SVG document and have it call a javascript function in the html page. onload maps to SVGLoad.
http://www.w3.org/TR/SVG11/interact.html#LoadEvent
The event is triggered at the point at which the user agent has fully parsed the element and its descendants and is ready to act appropriately upon that element
You could try polling every so often.
function checkReady() {
var svg = document.getElementById("chart").getSVGDocument();
if (svg == null) {
setTimeout("checkReady()", 300);
} else {
...
}
}