I am looking for a solution to show a progress indicator when an iframe page changes, but prior to the iframe loading completely.
There are 100's of pages on Stack Overflow going over the differences between jQuery ready(), JavaScript window.load, document.load, DOMContentReady, on('pageinit'...) and after reading the differences on all these various techniques I'm now a bit stuck on how to accomplish trapping the event within the iframe.
So far I have only succeeded in capturing when the iframe has changed once the DOM is built. I would like to be able to detect when a page is about to load so I could have some sort of indicator/spinner in my header.
This is what I have so far (capturing the iframe change on the onload):
.....
<iframe id="rssID" src="http://feeds.reuters.com/reuters/oddlyEnoughNews"
onload="blerg()" style="width: 800px; height:600px"/>
......
$(document).on('pageinit','#index', function(){
alert('pageinit'); //gets called on first load
});
$(document).on('pageinit','#rssID', function(){
alert('pageinit rssFeed'); //nothing happens.
});
function blerg() {
var myIframe = document.getElementById("rssID");
myIframe.onload = func;
};
function func() {
alert("changed");
}
http://jsfiddle.net/Gdxvs/
It would appear that pageinit is the correct path but I have no idea on how to trap that within an iframe. I would prefer not using jQuery, but I'm not sure that is possible without huge amounts of code.
One final: Do I need to use a die("pageinit");
Related
I apologize for the possibly naive nature of this question but I am not a web developer by day.
Is it possible to write a script such that, for any arbitrary web page, a function that I have written will be called if a URL is moused over? I was initially thinking that I could use document.links to assemble an array of all of the hrefs in a document and add an onmouseover event attribute to each of them but, unless I'm mistaken, that would overwrite any existing onmouseover attributes already present in the page. Not ideal.
I'm not sure if by arbitrary web page you mean any pages on any domains or any pages of your own domain, but for the latter you could put something like the following in your pages:
$(function () {
$(document).on('mouseenter', 'a', function () {
console.log(this, 'hovered');
});
});
If you mean any page your browse to on the net, then you will have to write a browser extension for the browser your are using. For Chrome have a look at this.
You could try getting everything with the a tag and inject an onmouseover.
window.onload = function(){
for(m=0;m<document.getElementsByTagName('a');m++){
if(document.getElementsByTagName('a')[m].className == 'someclass'){
document.getElementsByTagName('a')[m].onmouseover = function(){
Your Code
}
}
}
}
Here is the circumstance:
I have 2 pages:
1 x html page
1 x external Javascript
Now in the html page, there will be internal Javascript coding to allow the placement of the window.onload, and other page specific methods/functions.
But, in the external Javascript I want certain things to be done before the window.onload event is triggered. This is to allow customized components to be initialized first.
Is there a way to ensure initialization to occur in the external Javascript before the window.onload event is triggered?
The reason I have asked this, is to attempt to make reusable code (build once - use all over), to which the external script must check that it is in 'order/check' before the Javascript in the main html/jsp/asp/PHP page takes over. And also I am not looking for a solution in jQuery #_#
Here are some of the links on Stack Overflow I have browsed through for a solution:
Javascript - How to detect if document has loaded (IE 7/Firefox 3)
How to check if page has FULLY loaded(scripts and all)?
Execute Javascript When Page Has Fully Loaded
Can someone help or direct me to a solution, your help will be muchness of greatness appreciated.
[updated response - 19 November 2012]
Hi all, thanks for you advice and suggested solutions, they have all been useful in the search and testing for a viable solution.
Though I feel that I am not 100% satisfied with my own results, I know your advice and help has moved me closer to a solution, and may indeed aid others in a similar situation.
Here is what I have come up with:
test_page.html
<html>
<head>
<title></title>
<script type="text/javascript" src="loader.js"></script>
<script type="text/javascript" src="test_script_1.js"></script>
<script type="text/javascript" src="test_script_2.js"></script>
<script type="text/javascript">
window.onload = function() {
document.getElementById("div_1").innerHTML = "window.onload complete!";
}
</script>
<style type="text/css">
div {
border:thin solid #000000;
width:500px;
}
</head>
<body>
<div id="div_1"></div>
<br/><br/>
<div id="div_2"></div>
<br/><br/>
<div id="div_3"></div>
</body>
</html>
loader.js
var Loader = {
methods_arr : [],
init_Loader : new function() {
document.onreadystatechange = function(e) {
if (document.readyState == "complete") {
for (var i = 0; i < Loader.methods_arr.length; i++) {
Loader.method_arr[i]();
}
}
}
},
load : function(method) {
Loader.methods_arr.push(method);
}
}
test_script_1.js
Loader.load(function(){initTestScript1();});
function initTestScript1() {
document.getElementById("div_1").innerHTML = "Test Script 1 Initialized!";
}
test_script_2.js
Loader.load(function(){initTestScript2();});
function initTestScript2() {
document.getElementById("div_2").innerHTML = "Test Script 2 Initialized!";
}
This will ensure that scripts are invoked before invocation of the window.onload event handler, but also ensuring that the document is rendered first.
What do you think of this possible solution?
Thanking you all again for the aid and help :D
Basically, you're looking for this:
document.onreadystatechange = function(e)
{
if (document.readyState === 'complete')
{
//dom is ready, window.onload fires later
}
};
window.onload = function(e)
{
//document.readyState will be complete, it's one of the requirements for the window.onload event to be fired
//do stuff for when everything is loaded
};
see MDN for more details.
Do keep in mind that the DOM might be loaded here, but that doesn't mean that the external js file has been loaded, so you might not have access to all the functions/objects that are defined in that script. If you want to check for that, you'll have to use window.onload, to ensure that all external resources have been loaded, too.
So, basically, in your external script, you'll be needing 2 event handlers: one for the readystatechange, which does what you need to be done on DOMready, and a window.onload, which will, by definition, be fired after the document is ready. (this checks if the page is fully loaded).
Just so you know, in IE<9 window.onload causes a memory leak (because the DOM and the JScript engine are two separate entities, the window object never gets unloaded fully, and the listener isn't GC'ed). There is a way to fix this, which I've posted here, it's quite verbose, though, but just so you know...
If you want something to be done right away without waiting for any event then you can just do it in the JavaScript - you don't have to do anything for your code to run right away, just don't do anything that would make your code wait. So it's actually easier than waiting for events.
For example if you have this HTML:
<div id=one></div>
<script src="your-script.js"></script>
<div id=two></div>
then whatever code is in your-script.js will be run after the div with id=one but before the div with id=two is parsed. Just don't register event callbacks but do what you need right away in your JavaScript.
javascript runs from top to bottom. this means.. if you include your external javascript before your internal javascript it would simply run before the internal javascript runs.
It is also possible to use the DOMContentLoaded event of the Window interface.
addEventListener("DOMContentLoaded", function() {
// Your code goes here
});
The above code is actually adding the event listener to the window object, though it's not qualified as window.addEventListener because the window object is also the global scope of JavaScript code in webpages.
DOMContentLoaded happens before load, when images and other parts of the webpage aren't still fully loaded. However, all the elements added to the DOM within the initial call stack are guaranteed to be already added to their parents prior to this event.
You can find the official documentation here.
I need to execute some scripts when all the resources on my domain and subdomain are loaded, so I did this:
$(window).load(function(){
// al my functions here...
}
The problem is that there are some external resources (not on my domain and subdomain) that sometimes take longer to load. Is there a way to exclude external resources from the load event?
EDIT:
I was hoping to do something like:
$(window).not(".idontcare").load(function()
but it's not working
I guess your external resources rely on a src attribute.
If so, in your page source code you could set the src attribute of the resources you don't want to wait for, not as src but as external_src.
Then you could easily do:
$(document).ready(function(){
$(window).load(function(){
// all your functions here...
});
$('[external_src]').each(function() {
var external_src = $(this).attr("external_src");
$(this).attr("src", external_src); // now it starts to load
$(this).removeAttr("external_src"); // keep your DOM clean
//Or just one line:
//$(this).attr("src", $(this).attr("external_src")).removeAttr("external_src");
});
});
This way the external resources should start loading as soon as just the DOM is ready, without waiting for the full window load.
I have almost same case. But in my case, I want to exclude all iframes that load content from another site (e.g. youtube, vimeo etc). Found a work around, so the scenario is hide 'src' attribute from all iframes when DOM is ready and put it back when window is finish load all another content.
(function($){
//DOM is ready
$(document).ready(function(){
var frame = $('iframe'),
frameSrc = new Array();
if( frame.length ){
$.each( frame, function(i, f){
frameSrc[i] = $(f).attr('src');
//remove the src attribute so window will ignore these iframes
$(f).attr('src', '');
});
//window finish load
$(window).on('load',function(){
$.each( frame, function(a, x){
//put the src attribute value back
$(x).attr('src', frameSrc[a]);
});
});
}
});
})(jQuery);
You can mark all elements in your site that load external resources by adding a special class, and change the iframe with $('.special_class') or something like that. I dont know if this is the best way but at least it works great in my side :D
Unfortunately, the window.onload event is very strict. As you might know it will fire when all und every resource was transfered and loaded, images, iframes, everything. So the quick answer to your question is no, there is no easy-to-use way to tell that event to ignore external resources, it makes no difference there.
You would need to handle that yourself, which could be a tricky thing according to how those resources are included and located. You might even need to manipulate the source code before it gets delivered to accomplish that.
As far as I know, there is an async - tag for script tags. You can your includes to:
<script src="script_path" async="true"></script>
This will not include them to the event.
maybe
$(document).ready(...)
instead of $(window).load() will help?
The document ready event executes already when the HTML-Document is loaded and the DOM is ready, even if all the graphics haven’t loaded yet.
What is the best unobtrusive way of invoking something after the page is being loaded in plain JavaScript? Of course in jQuery I would use:
$(document).ready(function(){...});
but I am not sure about the most reliable approach in plain js.
Clearly
window.onload = ...
is not proper solution, because it would overwrite previous declaration.
What I am trying to do is to insert an iframe into a div after the page is loaded, but maybe there are actually better ways of doing it. My plan is to do something like:
window.onload = function(divId){
var div = document.getElementById(divId);
div.innerHTML = "<iframe src='someUrl' .. >";
}
EDIT:
Apologies for not including all necessary details.
The script is not for my website - the idea is to show a part of my site (a form) on external web sites. The priority is to minimize the effort someone has to put to use my code. That is why I would like to keep everything in js file and absolutely nothing in <script> - except of <script src="http://my.website/code.js" />. If I change URL of an iframe or I would like to add some features, I would like to update the code on all other web sites without asking them to make any changes.
My approach might be wrong - any suggestions are very welcome.
//For modern browsers:
document.addEventListener( "DOMContentLoaded", someFunction, false );
//For IE:
document.attachEvent( "onreadystatechange", someFunction);
`attachEvent` and `addEventListener` allow you to register more than one event listener for a particular target.
See:
https://developer.mozilla.org/en/DOM/element.addEventListener
Also definitly worth looking at how jQuery does it:
http://code.jquery.com/jquery-1.7.js Search for bindReady.
Use window.addEventListener and the events load or DOMContentLoaded:
window.addEventListener('DOMContentLoaded',function(){alert("first handler");});
window.addEventListener('DOMContentLoaded',function(){alert("second handler");});
object.addEventListener('event',callback) will insert an event listener into a queue for that specific object event. See https://developer.mozilla.org/en/DOM/element.addEventListener for further information.
For IE5-8 use window.attachEvent('event',callback), see http://msdn.microsoft.com/en-us/library/ms536343%28VS.85%29.aspx. You can build yourself a little helper function:
function addEventHandler(object,szEvent,cbCallback){
if(typeof(szEvent) !== 'string' || typeof(cbCallback) !== 'function')
return false;
if(!!object.addEventListener){ // for IE9+
return object.addEventListener(szEvent,cbCallback);
}
if(!!object.attachEvent){ // for IE <=8
return object.attachEvent(szEvent,cbCallback);
}
return false;
}
addEventHandler(window,'load',function(){alert("first handler");});
addEventHandler(window,'load',function(){alert("second handler");});
Note that DOMContentLoaded isn't defined in IE lesser 9. If you don't know your recipient's browser use the event load.
Just put your script include at the very end of the document, immediately before or after the ending </body> tag, e.g.:
(content)
(content)
<script src="http://my.website/code.js"></script>
</body>
</html>
All of the markup above the script will be accessible via the usual DOM methods (reference). Obviously, not all ancillary resources (images and such) will be fully loaded yet, but presumably that's why you want to avoid the window load event (it happens so late).
The only real purpose of ready-style events is if you don't control where the script gets included (e.g., libraries) or you need to have something execute prior to the page load and something else after the page load, and you want to avoid having two HTTP requests (e.g., for two different scripts, one before load and one after).
We use a modified version of Jiffy to measure actual client-side performance.
The most important thing we do is measure the time between when the request was received and when the page load event fires in the browser.
On some pages we have iframe elements that point to external sites that we don't control - they sometimes take a long while to load. At the moment, the page load event for our page fires only after the iframe is completely loaded (and it's own load event fires).
I'd like to separate these measurements - have one measurement after everything including the iframe is loaded, but also one measurement without the iframe - that is, when the page load would have occured if we didn't have an iframe.
The only way I've managed to do this so far is to add the iframe to the DOM after the page load event, but that delays the loading of the iframe.
Any ideas?
EDIT: bounty is over, thanks for the help and ideas! I chose Jed's answer because it gave me a new idea - start loading the iframes, but "pause" them so they won't affect page load (by temporarily setting src="about:blank"). I'll try to add a more detailed summary of my results.
You can achieve this for multiple iframes without dynamically adding them by:
setting the src attribute for all frames to about:blank before body load,
letting the body load event fire,
adding an onload handler to capture the load time of each frame, and then
restoring the src attribute of each frame to its original value.
I've created a frameTimer module that consists of two methods:
an init method that needs to be called immediately before the </body> tag, and
a measure method to be called on page load, which takes a callback with the results.
The results object is a hash like this:
{
iframes: {
'http://google.co.jp/': 1241159345061,
'http://google.com/': 1241159345132,
'http://google.co.uk/': 1241159345183,
'http://google.co.kr/': 1241159345439
},
document: 1241159342970
}
It returns integers for each load time, but could be easily changed to just return the diff from the document body load.
Here's a working example of it in action, with this javascript file (frameTimer.js):
var frameTimer = ( function() {
var iframes, iframeCount, iframeSrc, results = { iframes: {} };
return {
init: function() {
iframes = document.getElementsByTagName("iframe"),
iframeCount = iframes.length,
iframeSrc = [];
for ( var i = 0; i < iframeCount; i++ ) {
iframeSrc[i] = iframes[i].src;
iframes[i].src = "about:blank";
}
},
measure: function( callback ) {
results.document = +new Date;
for ( var i = 0; i < iframeCount; i++ ) {
iframes[i].onload = function() {
results.iframes[ this.src ] = +new Date;
if (!--iframeCount)
callback( results )
};
iframes[i].src = iframeSrc[ i ];
}
}
};
})();
and this html file (frameTimer.html):
<html>
<head>
<script type="text/javascript" src="frameTimer.js"></script>
</head>
<body onload="frameTimer.measure( function( x ){ alert( x.toSource() ) } )">
<iframe src="http://google.com"></iframe>
<iframe src="http://google.co.jp"></iframe>
<iframe src="http://google.co.uk"></iframe>
<iframe src="http://google.co.kr"></iframe>
<script type="text/javascript">frameTimer.init()</script>
</body>
</html>
This could be done in a lot less code (and less obtrusively) with jQuery, but this is a pretty lightweight and dependency-free attempt.
I'm not familiar with jiffy per se, but in the past I've done similar measurements using a crude bespoke function, roughly like this (5 mins typing from memory):
<html>
<head>
<script>
var timer = {
measure: function(label)
{
this._timings[label] = new Date();
},
diff: function(a,b)
{
return (this._timings[b] - this._timings[a]);
},
_timings: {}
}
</script>
</head>
<body onload="alert(timer.diff('iframe.start','iframe.done'));">
<!-- lorem ipsum etc -->
<script>timer.measure('iframe.start');</script>
<iframe src="http://www.google.com" onload="timer.measure('iframe.done');"/>
<!-- lorem ipsum etc -->
</body>
</html>
From that you can see the relevant part is simply to note a datestamp immediately before the iframe is encountered, and then add an event handler to the iframes' onload event (which will work regardless of the domain of the iframe source and doesn't require modifying the content) to take another measurement (normally I'd add these events with addEventListener/attachEvent but you get the idea).
That gives you a timespan for the iframe you can subtract from the total to give you a reasonable idea of loadtime with and without iframe.
HTH
I am not sure if it will help you:
As jQuery user I get my code executed long before the onLoad event of the window fires.
It is an essential functionality of jQuery to allow processing as soon as the DOM of the loaded page is ready (see: jQuery Ready Event)
I then can add event handlers to every element which may finish loading "later" including the iframes.
But I understand that this may not be enough for your case as you want to have the timing of "everything" as without loading of the iframes. I am not sure what happens to the onLoad event of the window if you set the iframe sources out of the jQuery ready handler. I guess they will load independently but that needs to be checked.
On the other side... this ready function shows when people will see your page "building up". Loading of images and stuff is mostly secondary for perception of humans. That is why windows shows the desktop before it is finished loading for a while now :-)
I'll suggest the "domready" event as OderWat suggested with one exception: you do not need jQuery to implement this event. At http://dean.edwards.name/weblog/2006/06/again/ it is well explained for All three(actually 4) major browsers.
Actually, if you run your tests on a specific browser, it is easier for you to implement this event for tracking.
Hope it helps.