Adding multiple onload handlers - javascript

I have two js files, each one with its own window.onload handler. Depending on how I attach the two onload handlers to the window object I get a different behaviour on the second handler.
More specifically, here is my html file:
<html>
<head>
<title>Welcome to our site</title>
<script type="text/javascript" src="script1.js"> </script>
<script type="text/javascript" src="script2.js"> </script>
</head>
<body id="pageBody">
<h2 align="center">
Wellcome to our site... c'mon in!
</h2>
</body>
</html>
It loads two js files, script1.js and script2.js.
Here is the version of these two scripts that leads to the (at least by me) unexpected behaviour.
Script1.js:
window.onload = initAll1(); // attach first onload handler
function initAll1() {
alert("initAll1");
document.getElementById("redirect").onclick = foo; // attach an onclick handler
}
function foo() {
alert("we are in foo");
return false;
}
Script2.js:
addOnloadHandler(initAll2); // with this we should attach a second onload handler
function initAll2() {
alert("initAll2");
if (linkHasOnclickHandler(document.getElementById("redirect"))) {
alert("correct!");
}
else {
alert("wrong!");
}
}
function addOnloadHandler (newFunction) {
var oldevent = window.onload;
if (typeof oldevent == "function") {
window.onload = function() {
if (oldevent) {
oldevent();
}
newFunction();
};
}
else {
window.onload = newFunction;
}
}
function linkHasOnclickHandler() {
var oldevent = document.getElementById("redirect").onclick;
if (typeof oldevent == "function") {
return true;
}
else {
return false;
}
}
In Script2.js I tried to add the second onload handler in a nice noninvasive way using function addOnloadHandler(). This function does not make any assumption on whether there is already any onload handler attached to the window object. It is noninvasive because it should add the new handler without deleting previous ones.
The thing is that when loaded with addOnloadHandler(), initAll2() is not capable of detecting the fact that document.getElementById("redirect") already has foo() attached as an onclick event handler (see initAll1()). The alert message "wrong!" is triggered, which to me seems to be the wrong behaviour.
When I forget about addOnloadHandler() and attach both onload handlers in Script1.js using:
window.onload = function () {initAll1(); initAll2();};
then everything works as expected, and initAll2() launches the "correct!" alert message.
Is there something wrong about addOnloadHandler()? Could anybody make it work? I would really like to use it instead of the second method.
Thanks!

Just in case future people find this, and are looking for a way to use multiple event handlers when the object itself doesn't support addEventListener, attachEvent or some other form of listener stacking - i.e. it is a bespoke object, badly implemented. Then you can do the following:
object.onload = (function(pre){
return function(){
pre && pre.apply(this,arguments);
/// do what you need for your listener here
}
})(object.onload);
Each time you use the above code the previous onload listener is passed in as an argument, and when your new listener is triggered it runs the old listener first - meaning you can stack many listeners like this, if you so wish. However, this will only work for as long as the above code is always used to add listeners to your object. All your hard work will be undone if somewhere else it is overridden with a simple:
object.onload = function(){}
As a note to coders, if you are to implement a library, plugin or constructor, and it is possible other coders will take over your work. Please, please code the ability for multiple event listeners. It's really not that difficult.

You need to look at addEventListener and attachEvent, which are native implementations of your addOnloadHandler.

PPK's reference on addEventListener explains how do this pretty well:
http://www.quirksmode.org/js/events_advanced.html

Thanks for the answers!
I rewrote my script2.js using addEventListener and attachEvent like this:
//addOnloadHandler(initAll1); // it works only when uncommenting this
addOnloadHandler(initAll2);
function initAll2() {
alert("initAll2");
if (linkHasOnclickHandler(document.getElementById("redirect"))) {
alert("correct!");
}
else {
alert("wrong!");
}
}
function addOnloadHandler(newFunction) {
if (window.addEventListener) { // W3C standard
window.addEventListener('load', newFunction, false); // NB **not** 'onload'
}
else if (window.attachEvent) { // Microsoft
window.attachEvent('onload', newFunction);
}
}
function linkHasOnclickHandler(element) {
var oldevent = document.getElementById("redirect").onclick;
if (typeof oldevent == "function") {
return true;
}
else {
return false;
}
}
As you can see, addOnloadHandler() has been rewritten using the native implementations you guys mentioned. I left script1.js untouched.
The resulting code still does not work (i.e., the "wrong!" alert message is shown). It only works when I register the onload initAll1() handler twice by uncommenting the first line of code in script2.js.
Apparently, mixing
window.onload = handler1;
and
window.addEventListener('load', handler2, false);
or
window.attachEvent('onload', handler2);
does not work fine.
Is there any way to work around this problem that does not imply touching script1.js?
Just in case you wonder why I don't want to touch script1.js, the reason is that I want my code (script2.js) to be reusable in other projects as well, no matter which other js files each project uses. So, it should work with every possible event-handling registration method used in script1.js.
thanks once more for your help!

Related

JS - Elements still null after body.onload

According to this question, by the time the body onload gets called, all of the objects in the document are in the DOM, and all the images and sub-frames have finished loading.
I have some JS that is supposed to fire at body onload but my attempts to getElementByID are returning null which makes me think it's firing earlier than I think it is.
Am I wrong in my interpretation of body.onload or am I doing something else wrong?
Here is my code:
<script type="text/javascript">
window.document.body.onload = doStuff;
function doStuff() {
var txtElement = document.getElementById("myTextField");
if (txtElement != null) {
alert(txtElement.value);
}
else {
alert("Element not found!"); //This alert is always thrown
}
}
</script>
You are actually calling the doStuff function before the DOM is loaded because you have parenthesis after the function name, which causes the function to be invoked as soon as that line is encountered.
When you register a function as a callback to an event, you just want to reference the function, not invoke it. Also, you only need to register the "page" load function to the window.
Change:
window.document.body.onload = doStuff();
to:
window.onload = doStuff;
Additionally, you should modernize your code and, instead of using an event property of an element (which only allows for one function to be stored as a callback to an event), you should use the addEventListener() method to register callback functions.
Lastly, the type attribute is no longer necessary on script tags.
<script>
window.addEventListener("load", doStuff);
function doStuff() {
var txtElement = document.getElementById("myTextField");
if (txtElement != null) {
alert(txtElement.value);
}
else {
alert("Element not found!"); //This alert is always thrown
}
}
</script>

Run a function when another function is called

I'm wondering how to run a function when another function is called. addEventListener only runs events like "click", "mouseover", etc. However, I'd like to listen for a function call.
EXAMPLE:
Function 1 is called. Afterwards, Function 2 runs because it saw that Function 1 was called.
Is there an addEventListener alternative for simple functions and not events? I can't seem to find any.
My goal was to simply run a function everytime a user did something like call for when something was hidden in jQuery or by another JavaScript library or just simply another external JavaScript file with some code I added in.
Introducing a very hacky way
Since what you are trying to achieve is basically hacking some existing system (you shouldn't run into this problem if you have control over both sides and design your code properly).
It looks like your function is declared globally as well. In that case:
1. store the existing function in a variable
2. overwrite that function with your implementation
3. call the function variable at the start
function myFunction(){
//This is the main function
alert('Hello, this is part of the message!');
}
var tempfunc = myFunction;
window.myFunction = function() {
tempfunc();
// do what you need to do in the event listener here
alert('Hello, this is the other part of the message!');
}
EDIT:
The original question had the requirement that the original function cannot be modified, hence my solution. Since they it appears the question has changed.
You will have trigger an event inside myFunction and listen to that event.
function myFunction(){
//This is the main function
alert('Hello, this is part of the message!');
// trigger the event
var event = new CustomEvent("event", { "detail": "Example of an event" });
document.dispatchEvent(event);
}
// handle here;
document.addEventListener("event", function(){
//This is the secondary function
//or the function I need to run after the main function is called
alert('Hello, this is the other part of the message!');
});
// call main
myFunction();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Simple test!</p>
<p>Long story short, you get message 1, but message 2 never pops up!</p>
Is there an addEventListener alternative for simple functions and not
events? I can't seem to find any.
My goal was to simply run a function everytime a user did something
like call for when something was hidden in jQuery or by another
JavaScript library or just simply another external JavaScript file
with some code I added in.
You can use jQuery.Callbacks()
var callbacks = $.Callbacks();
function handleCallback1(message) {
console.log(message, this)
};
function handleCallback2(message) {
if (this.tagName === "DIV") {
this.style.color = "green";
} else {
this.nextElementSibling.style.color = "blue";
}
};
$("button, div").on("click", function() {
callbacks.fireWith(this, ["called from " + this.tagName])
});
callbacks.add(handleCallback1, handleCallback2);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<button>click</button>
<div>click</div>

What happens if multiple scripts set window.onload?

There are a number of posts on StackOverflow and other websites regarding the problem of avoiding namespace collisions. In my scenario, I just want a method in my JavaScript to be executed after the DOM is accessible.
If I do the following will it avoid namespace collisions?
<script type="text/javascript">window.onload = function() { //Define my namespace var here, and execute all my code }</script>
What if a script that is injected later also sets an onload function ? Will mine get overwritten? I'm fully aware that I can test this out, but I would also like some feedback as I am new to JavaScript and there could be a number of other scenarios which will do the something that I am not aware of.
EDIT: I need to support only Safari 5.0+
Yes, the last one will overwrite the previous ones.
The solution: use the new event API: addEventListener.
This is a fine Javascript way to do it right
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(nameOfSomeFunctionToRunOnPageLoad);
addLoadEvent(function() {
/* more code to run on page load */
});
Explained Source
There's lots of information on this, but here's the short version:
if you want to play nicely with onload, you can do
var prev_onLoad = window.onload;
window.onload = function() {
if (typeof(prev_onLoad)=='function')
prev_onLoad();
// rest of your onLoad handler goes here
}
and hope that other's play nicely or make sure that's the last setting of onload in the code.
However, more modern browsers have event registration functions (addEventListener and attachEvent on IE) which take care of this chaining among other things. Quite a few cross-browser onload event functions have been written which take care of this logic for you.
It'll be overriden .
In Javascript, when you define handle event like
window.onload = function(){
console.log("in Load function 1");
};
window.onload = function(){
console.log(" In load function 2");
};
That will make an " assign " window.onload => function() . And window.onload will be assign to last function .
But in jQuery,
You can handle event in many times and the browser will make all
$("body").on("click",function(){
console.log("make a callback function 1");
});
$("body").on("click",function(){
console.log("make a callback function 2");
});
Because jQuery make a callback not "assign".
Hope it helps you.

Anyway to use two .onclick event handlers from two different scripts on one button?

Is it possible to use two .onclick event handlers on the same button when the event handlers are stored in two different scripts? I know it would be easy to just call the two functions I need into the same .onclick handler, but I'm curious if there's a way around this.
Suppose I have
script1.js
someButton.onclick = function() {
someFunction1();
}
script2.js
someButton.onclick = function() {
someFunction2();
}
Running these scripts in the html in this order would lead to calling someFunction2() onclick, as I assume the functionality of the button is overwritten by script2.js.
You can use addEventListener and this will add both the event handlers to the element on contrast to replacing the onclick attribute of the element.
someButton.addEventListener('click', function() {
someFunction1();
});
someButton.addEventListener('click', function() {
someFunction2();
});
Fiddle
addEventListener is the way to go here, but if you were to stick with someButton.onclick, I would do this:
var old = someButton.onclick;
someButton.onclick = function (event) {
if (typeof old === 'function') {
old.call(someButton, event); // call the old listener
}
someFunction2(); // run new code
};
It saves a reference to the old onclick function, and makes a new onclick function that will not only call the old onclick function, but run whatever other code you want as well.

How to fire an event immediately?

I'm binding an event like this, using prototype js:
$('country').observe('change',function(e) { ... });
How can I fire it once immediately?
in jQuery, I'd just tack on a .triggerHandler('change'). Is there something similar in prototype?
Use the load event. Something like this:
// calls addListeners when the document loads
Event.observe(window, 'load', addListeners, false);
function addListeners() {
// called onLoad
fireOnce();
// observer for the country dropdown
$('country').observe('change', function(event) {
fireOnChange();
});
}
function fireOnce() {
// do something
}
function fireOnChange() {
// do something
}
When the document loads, fireOnce() will execute. I use this technique all the time.
If using an extension is an option, I have had success in the past with event.simulate for this purpose.
It'll allow you to do something like:
$('country').simulate('change');
Try this:
var handler = function(e) {...};
$("country").observe("change",handler);
handler();
Alternatively (less readable, avoids temporary variable):
$("country").observe("change",(function(e) { ... return arguments.callee;})());
However, in both cases you will not be able to use this as you might expect. This solution is better suited to more general callbacks such as for setInterval
...if you know that it exists, and you know that you're not waiting for pageload or waiting for a script to load, why not just:
(function (el) {
if (!el) { return; }
doSomething(el);
}(document.getElementById("country")));

Categories