I am having trouble with some JavaScript running before the page is completely rendered in IE 6 (maybe other versions too but just testing IE6 for now. Firefox seems to be OK). I can get around this by calling the js on window.onload like this:
window.onload = function(){doIt();}
However, my concern is the fact that I will overwrite anything else that may already be in window.onload. The code will be used as part of a library so I can not guarantee that window.onload will not be set somewhere else by someone else. I would rather append my function to the onload event like this:
window.onload += function(){doIt1();}
window.onload += function(){doIt2();}
But when I do so, only doit2() is called. Is there a way to register an event handler for when the page is fully rendered? My second thought would be to just put my code in a loop checking to make sure all my objects exist before running. But I am scared that this could potentially lockup the browser.
Just for some background info, my code is hiding/showing iFrames. I know that I can use the iFrame's onload attribute but I need all of the iFrames to be fully loaded before calling the code.
Any thoughts from the community? Thanks in advance for you input.
Use this generic addLoadEvent function...
function addLoadEvent(func) {
if(typeof window.onload != 'function')
window.onload = func;
else {
var oldLoad = window.onload;
window.onload = function() {
if(oldLoad) oldLoad();
func();
}
}
}
This essentially queues up functions to be executed. It never overwrites a previously assigned handler. Sample usage below...
addLoadEvent(function() { alert("One!"); });
addLoadEvent(two);
function two() {
alert("Two!");
}
I want to mention that libraries like jQuery take care of known issues like this for you.
Related
I want to call a function when the browser opens (or reloads)
If I use:
window.onopen = window.alert("opened");
That works fine however:
window.onopen = winopen;
window.onresize = winopen;
function winopen()
{
alert("opened");
}
Does not. The window.onresize call does work, I'm also using the resize event to call a different function but put it in here just to test whether the function call worked at all as I have the resize event function call working elsewhere.
That's because you're misunderstanding a fundamental thing:
window.onopen = window.alert("opened");
Is assigning window.onopen the result of calling window.alert("opened"), which will call the function regardless.
The second way is the correct way of assigning functions, but there is no such event as onopen.
If you want to call a function on each page load, just call it in your .js file without anything else. The file will be parsed and executed every time the page loads.
So basically, you want:
window.onresize = winopen;
function winopen()
{
alert("opened");
}
winopen(); //Call it now.
In some cases, you may want for the DOM to finish loading (all HTML was parsed, browser is ready for DOM manipulation), in that case, you can use window.onload instead of window.onopen in your example.
I'm wondering how to add another method call to the window.onload event once it has already
been assigned a method call.
Suppose somewhere in the script I have this assignment...
window.onload = function(){ some_methods_1() };
and then later on in the script I have this assignment
window.onload = function(){ some_methods_2() };
As it stands, only some_methods_2 will be called. Is there any way to add to the previous window.onload callback without cancelling some_methods_1 ? (and also without including both some_methods_1() and some_methods_2() in the same function block).
I guess this question is not really about window.onload but a question about javascript in general. I DON'T want to assign something to window.onload in such a way that that if another developer were to work on the script and add a piece of code that also uses window.onload (without looking at my previous code), he would disable my onload event.
I'm also wondering the same thing about
$(document).ready()
in jquery.
How can I add to it without destroying what came before, or what might come after?
If you are using jQuery, you don't have to do anything special. Handlers added via $(document).ready() don't overwrite each other, but rather execute in turn:
$(document).ready(func1)
...
$(document).ready(func2)
If you are not using jQuery, you could use addEventListener, as demonstrated by Karaxuna, plus attachEvent for IE<9.
Note that onload is not equivalent to $(document).ready() - the former waits for CSS, images... as well, while the latter waits for the DOM tree only. Modern browsers (and IE since IE9) support the DOMContentLoaded event on the document, which corresponds to the jQuery ready event, but IE<9 does not.
if(window.addEventListener){
window.addEventListener('load', func1)
}else{
window.attachEvent('onload', func1)
}
...
if(window.addEventListener){
window.addEventListener('load', func2)
}else{
window.attachEvent('onload', func2)
}
If neither option is available (for example, you are not dealing with DOM nodes), you can still do this (I am using onload as an example, but other options are available for onload):
var oldOnload1=window.onload;
window.onload=function(){
oldOnload1 && oldOnload1();
func1();
}
...
var oldOnload2=window.onload;
window.onload=function(){
oldOnload2 && oldOnload2();
func2();
}
or, to avoid polluting the global namespace (and likely encountering namespace collisions), using the import/export IIFE pattern:
window.onload=(function(oldLoad){
return function(){
oldLoad && oldLoad();
func1();
}
})(window.onload)
...
window.onload=(function(oldLoad){
return function(){
oldLoad && oldLoad();
func2();
}
})(window.onload)
You can use attachEvent(ie8) and addEventListener instead
addEvent(window, 'load', function(){ some_methods_1() });
addEvent(window, 'load', function(){ some_methods_2() });
function addEvent(element, eventName, fn) {
if (element.addEventListener)
element.addEventListener(eventName, fn, false);
else if (element.attachEvent)
element.attachEvent('on' + eventName, fn);
}
There are basically two ways
store the previous value of window.onload so your code can call a previous handler if present before or after your code executes
using the addEventListener approach (that of course Microsoft doesn't like and requires you to use another different name).
The second method will give you a bit more safety if another script wants to use window.onload and does it without thinking to cooperation but the main assumption for Javascript is that all the scripts will cooperate like you are trying to do.
Note that a bad script that is not designed to work with other unknown scripts will be always able to break a page for example by messing with prototypes, by contaminating the global namespace or by damaging the dom.
This might not be a popular option, but sometimes the scripts end up being distributed in various chunks, in that case I've found this to be a quick fix
if(window.onload != null){var f1 = window.onload;}
window.onload=function(){
//do something
if(f1!=null){f1();}
}
then somewhere else...
if(window.onload != null){var f2 = window.onload;}
window.onload=function(){
//do something else
if(f2!=null){f2();}
}
this will update the onload function and chain as needed
A pure JavaScript (no jQuery) method that would not override existing onload events but instead add to it, would be:
window.addEventListener('load', function() {
// do your things here
}
Is there an event that fires when JavaScript files are loaded? The problem came up because YSlow recommends to move JavaScript files to the bottom of the page. This means that
$(document).ready(function1) is fired before the js file that contains the code for function1 is loaded.
How to avoid this kind of situation?
I don't have a reference for it handy, but script tags are processed in order, and so if you put your $(document).ready(function1) in a script tag after the script tags that define function1, etc., you should be good to go.
<script type='text/javascript' src='...'></script>
<script type='text/javascript' src='...'></script>
<script type='text/javascript'>
$(document).ready(function1);
</script>
Of course, another approach would be to ensure that you're using only one script tag, in total, by combining files as part of your build process. (Unless you're loading the other ones from a CDN somewhere.) That will also help improve the perceived speed of your page.
EDIT: Just realized that I didn't actually answer your question: I don't think there's a cross-browser event that's fired, no. There is if you work hard enough, see below. You can test for symbols and use setTimeout to reschedule:
<script type='text/javascript'>
function fireWhenReady() {
if (typeof function1 != 'undefined') {
function1();
}
else {
setTimeout(fireWhenReady, 100);
}
}
$(document).ready(fireWhenReady);
</script>
...but you shouldn't have to do that if you get your script tag order correct.
Update: You can get load notifications for script elements you add to the page dynamically if you like. To get broad browser support, you have to do two different things, but as a combined technique this works:
function loadScript(path, callback) {
var done = false;
var scr = document.createElement('script');
scr.onload = handleLoad;
scr.onreadystatechange = handleReadyStateChange;
scr.onerror = handleError;
scr.src = path;
document.body.appendChild(scr);
function handleLoad() {
if (!done) {
done = true;
callback(path, "ok");
}
}
function handleReadyStateChange() {
var state;
if (!done) {
state = scr.readyState;
if (state === "complete") {
handleLoad();
}
}
}
function handleError() {
if (!done) {
done = true;
callback(path, "error");
}
}
}
In my experience, error notification (onerror) is not 100% cross-browser reliable. Also note that some browsers will do both mechanisms, hence the done variable to avoid duplicate notifications.
When they say "The bottom of the page" they don't literally mean the bottom: they mean just before the closing </body> tag. Place your scripts there and they will be loaded before the DOMReady event; place them afterwards and the DOM will be ready before they are loaded (because it's complete when the closing </html> tag is parsed), which as you have found will not work.
If you're wondering how I know that this is what they mean: I have worked at Yahoo! and we put our scripts just before the </body> tag :-)
EDIT: also, see T.J. Crowder's reply and make sure you have things in the correct order.
Take a look at jQuery's .load() http://api.jquery.com/load-event/
$('script').load(function () { });
Further to #T.J. Crowder 's answer, I've added a recursive outer loop that allows one to iterate through all the scripts in an array and then execute a function once all the scripts are loaded:
loadList([array of scripts], 0, function(){// do your post-scriptload stuff})
function loadList(list, i, callback)
{
{
loadScript(list[i], function()
{
if(i < list.length-1)
{
loadList(list, i+1, callback);
}
else
{
callback();
}
})
}
}
Of course you can make a wrapper to get rid of the '0' if you like:
function prettyLoadList(list, callback)
{
loadList(list, 0, callback);
}
Nice work #T.J. Crowder - I was cringing at the 'just add a couple seconds delay before running the callback' I saw in other threads.
I always make a call from the end of the JavaScript files for registering its loading and it used to work perfect for me for all the browsers.
Ex: I have an index.htm, Js1.js and Js2.js. I add the function IAmReady(Id) in index.htm header and call it with parameters 1 and 2 from the end of the files, Js1 and Js2 respectively. The IAmReady function will have a logic to run the boot code once it gets two calls (storing the the number of calls in a static/global variable) from the two js files.
Change the loading order of your scripts so that function1 was defined before using it in ready callback.
Plus I always found it better to define ready callback as an anonymous method then named one.
Like T.J. wrote: the order is defined (at least it's sequential when your browser is about to execute any JavaScript, even if it may download the scripts in parallel somehow). However, as apparently you're having trouble, maybe you're using third-party JavaScript libraries that yield some 404 Not Found or timeout? If so, then read Best way to use Google’s hosted jQuery, but fall back to my hosted library on Google fail.
I'm writing a js script that people will add to their web site by adding a single line of code to the header or end of the body part of their HTML.
My question is how to do the onload right on the external js file. Will the code below work? Can't it possibly run after the onload of the document and miss the onload event?
function c_onload () { alert ('onload'); }
if (window.attachEvent) {window.attachEvent('onload', c_onload);}
else if (window.addEventListener) {window.addEventListener('load', c_onload, false);}
else {document.addEventListener('load', c_onload, false);}
(I can't use Jquery or any other library)
What is your last else-clause
else {document.addEventListener('load', c_onload, false);
for? It's rather useless, imho.
The following should be a cross-browser solution: It first checks for addEventListener(), then attachEvent() and falls back to onload = ...
function chain(f1, f2) {
return typeof f1 !== 'function' ? f2 : function() {
var r1 = f1.apply(this, arguments),
r2 = f2.apply(this, arguments);
return typeof r1 === 'undefined' ? r2 : (r1 && r2);
};
}
function addOnloadListener(func) {
if(window.addEventListener)
window.addEventListener('load', func, false);
else if(window.attachEvent)
window.attachEvent('onload', func);
else window.onload = chain(window.onload, func);
}
Also, what kgiannakakis stated
The reason is that browsers handle the onLoad event differently.
is not true: all major browsers handle window.onload the same way, ie the listener function gets executed after the external resources - including your external script - have been loaded. The problem lies with DOMContentLoaded - that's where the hacks with doScroll(), defer, onreadystatechange and whatever else someone has cooked up come to play.
Depending on your target audience, you may either want to drop the fallback code or even use it exclusively. My vote would go for dropping it.
I am afraid that if you can't use jQuery or some other library you need to reproduce a way good deal of their functionality. The reason is that browsers handle the onLoad event differently.
I recommend that you download jQuery's code and see how the documentready function is implemented.
The onLoad event is supposed to be run when the element it is attached to is loaded. But some browsers* misinpret this as "beforeload" or "sometime during load" so the safest option to be sure something is run after all html is loaded, is to add a call to the function on the bottom of the HTML source, like this:
...
<script type="text/javascript">
c_onload();
</script>
</body>
</html>
(* at least some versions of Safari for Windows I do beleave have this issue)
In my ASP.NET User Control I'm adding some JavaScript to the window.onload event:
if (!Page.ClientScript.IsStartupScriptRegistered(this.GetType(), onloadScriptName))
Page.ClientScript.RegisterStartupScript(this.GetType(), onloadScriptName,
"window.onload = function() {myFunction();};", true);
My problem is, if there is already something in the onload event, than this overwrites it. How would I go about allowing two user controls to each execute JavaScript in the onload event?
Edit: Thanks for the info on third party libraries. I'll keep them in mind.
Most of the "solutions" suggested are Microsoft-specific, or require bloated libraries. Here's one good way. This works with W3C-compliant browsers and with Microsoft IE.
if (window.addEventListener) // W3C standard
{
window.addEventListener('load', myFunction, false); // NB **not** 'onload'
}
else if (window.attachEvent) // Microsoft
{
window.attachEvent('onload', myFunction);
}
There still is an ugly solution (which is far inferior to using a framework or addEventListener/attachEvent) that is to save the current onload event:
function addOnLoad(fn)
{
var old = window.onload;
window.onload = function()
{
old();
fn();
};
}
addOnLoad(function()
{
// your code here
});
addOnLoad(function()
{
// your code here
});
addOnLoad(function()
{
// your code here
});
Note that frameworks like jQuery will provide a way to execute code when the DOM is ready and not when the page loads.
DOM being ready means that your HTML has loaded but not external components like images or stylesheets, allowing you to be called long before the load event fires.
I had a similar problem today so I solved it having an index.js with the following:
window.onloadFuncs = [];
window.onload = function()
{
for(var i in this.onloadFuncs)
{
this.onloadFuncs[i]();
}
}
and in additional js files that i want to attach the onload event I just have to do this:
window.onloadFuncs.push(function(){
// code here
});
I normally use jQuery though, but this time I was restricted to pure js wich forced to use my mind for a while!
Mootools is another great JavaScript framework which is fairly easy to use, and like RedWolves said with jQuery you can can just keep chucking as many handlers as you want.
For every *.js file I include I just wrap the code in a function.
window.addEvent('domready', function(){
alert('Just put all your code here');
});
And there are also advantages of using domready instead of onload
Try this:
window.attachEvent("onload", myOtherFunctionToCall);
function myOtherFunctionToCall() {
// do something
}
edit: hey, I was just getting ready to log in with Firefox and reformat this myself! Still doesn't seem to format code for me with IE7.
I don't know a lot about ASP.NET, but why not write a custom function for the onload event that in turn calls both functions for you? If you've got two functions, call them both from a third script which you register for the event.
Actually, according to this MSDN page, it looks like you can call this function multiple times to register multiple scripts. You just need to use different keys (the second argument).
Page.ClientScript.RegisterStartupScript(
this.GetType(), key1, function1, true);
Page.ClientScript.RegisterStartupScript(
this.GetType(), key2, function2, true);
I believe that should work.
You can do this with jquery
$(window).load(function () {
// jQuery functions to initialize after the page has loaded.
});