I develop Joomla websites/components/modules and plugins and every so often I require the ability to use JavaScript that triggers an event when the page is loaded. Most of the time this is done using the window.onload function.
My question is:
Is this the best way to trigger JavaScript events on the page loading or is there a better/newer way?
If this is the only way to trigger an event on the page loading, what is the best way to make sure that multiple events can be run by different scripts?
window.onload = function(){}; works, but as you might have noticed, it allows you to specify only 1 listener.
I'd say the better/newer way of doing this would be to use a framework, or to just to use a simple implementation of the native addEventListener and attachEvent (for IE) methods, which allows you to remove the listeners for the events as well.
Here's a cross-browser implementation:
// Cross-browser implementation of element.addEventListener()
function listen(evnt, elem, func) {
if (elem.addEventListener) // W3C DOM
elem.addEventListener(evnt,func,false);
else if (elem.attachEvent) { // IE DOM
var r = elem.attachEvent("on"+evnt, func);
return r;
}
else window.alert('I\'m sorry Dave, I\'m afraid I can\'t do that.');
}
// Use: listen("event name", elem, func);
For the window.onload case use: listen("load", window, function() { });
EDIT I'd like to expand my answer by adding precious information that was pointed by others.
This is about the DOMContentLoaded (Mozilla, Opera and webkit nightlies currently support this) and the onreadystatechange (for IE) events which can be applied to the document object to understand when the document is available to be manipulated (without waiting for all the images/stylesheets etc.. to be loaded).
There are a lot of "hacky" implementations for cross-browsers support of this, so I strongly suggest to use a framework for this feature.
The window.onload events are overridden on multiple creations. To append functions use the
window.addEventListener(W3C standard) or the window.attachEvent(for IE). Use the following code which worked.
if (window.addEventListener) // W3C standard
{
window.addEventListener('load', myFunction, false); // NB **not** 'onload'
}
else if (window.attachEvent) // Microsoft
{
window.attachEvent('onload', myFunction);
}
Modern javascript frameworks have introduced the idea of a "document ready" event. This is an event that will fire when the document is ready to have DOM manipulations performed on it. The "onload" event fires only after EVERYTHING on the page has loaded.
Along with the "document ready" event, the frameworks have provided a way to queue up multiple bits of Javascript code and functions to run when the event fires.
So, if you're opposed to frameworks, the best way to go about this is to implement your own document.onload queue.
If you're not opposed to frameworks, then you'll want to look into jQuery and document.ready, Prototype and dom:loaded, Dojo and addOnLoad or Google for [your framework] and "document ready",.
If you haven't picked a framework but are interested, jQuery is a good place to start. It doesn't change any of Javascript's core functionality, and will generally stay out of your way and let you do things as you like when you want to.
Joomla ships with MooTools, so you'll find it easiest to use the MooTools library for your additional code. MooTools ships with a custom event called domready that fires when the page is loaded and the document tree is parsed.
window.addEvent( domready, function() { code to execute on load here } );
More information about MooTools can be found here. Joomla 1.5 currently ships with MT1.1 while the Joomla 1.6 alpha will include MT1.2
Personally, I prefer this method. Not only does it allow you to have multiple onload functions, but if some script had defined it before you got to it, this method is nice enough to handle that... The only problem left is if a page loads several script which does not use the window.addLoad() function; but that is their problem :).
P.S. Its also great if you want to "chain" more functions later on.
This is the dirty but shorter way :P
function load(){
*code*
}
window[ addEventListener ? 'addEventListener' : 'attachEvent' ]
( addEventListener ? 'load' : 'onload', function(){} )
As of I always include jQuery/bootstrap JS files on bottom of document and have no acces to $ over the document, I developed a tiny "init" plugin which is one and only JS script I include on very top of my pages:
window.init = new function() {
var list = [];
this.push = function(callback) {
if (typeof window.jQuery !== "undefined") {
callback();
} else {
list.push(callback);
}
};
this.run = function() {
if (typeof window.jQuery !== "undefined") {
for(var i in list) {
try {
list[i]();
} catch (ex) {
console.error(ex);
}
}
list = [];
}
};
if (window.addEventListener) {
window.addEventListener("load", this.run, false);
} else if (window.attachEvent) {
window.attachEvent("onload", this.run);
} else {
if (window.onload && window.onload !== this.run) {
this.push(window.onload);
}
window.onload = this.run;
}
};
Using this I can define any anonymous loader over the page, before jQuery and bootstrap inclusion and stay sure that it will fire once jQuery is present:
init.push(function() {
$('[name="date"]').datepicker({
endDate: '0d',
format: 'yyyy-mm-dd',
autoclose: true
}).on('hide', function() {
// $.ajax
});
$('[name="keyword_id"]').select2();
});
Related
I'm building a site and I have loads of scripts going on there, theyre all contained in different files and some of them are onload functions. I know about using a function to run all of them. However this doesn't work so well with multiple files. I've tried using an onload.js file:
window.onload = function() {
// Site wide funcs
searchShow();
// Page specific
if(getProducts && loadMore){
getProducts();
loadMore();
}
if(checkBox){
checkBox();
}
if(styleProduct) {
styleProduct();
}
}
where it should check if the function exists on the page before running it. (Some files are referenced in each file some are site wide and are referenced in the header file)
Could anyone suggest a better option for having all these onload files?
In your script files stick to addEventListener or attachEvent, as both allow multiple handlers for single event.
EDIT
As these two functions are browser specific, it is a good idea to start with some kind of wrapper:
function createEvent(objectTarget, eventName, eventHandler)
{
if (objectTarget.addEventListener)
objectTarget.addEventListener(eventName,eventHandler,false);
else if (objectTarget.attachEvent)
objectTarget.attachEvent('on'+eventName,genericHandler);
}
Now use createEvent to attach new handler to specific event.
createEvent(window, 'load', handler1);
createEvent(window, 'load', handler2);
...
Please take into account, that there are more severe differences in event model between browser (such as where is event object or target vs. srcElement).
I'm coding a script that will be used on several websites as a plugin. I have to use only Javascript, no framework. I'm asking myself, what is the best way to load my script without causing any trouble with other scripts or frameworks that can be loaded on these websites.
I thought to do a global function where i call the functions I want to use, and I put this function on the window.load event, like this :
<script>
var global = function(){
object.domain.function1();
object.domain.function2(param1, param2);
object.domain.function3(1);
}
(function(){
if(document.addEventListener){
document.addEventListener('load',global, false);
}
if(document.attachEvent){
document.attachEvent('onload',global);
}
})();
</script>
Or simply :
<script>
object.domain.function1();
object.domain.function2(param1, param2);
object.domain.function3(1);
</script>
Of course, I need some elements are loaded on my page.
Thanks in advance.
If you're ok with putting javascript inside the <body> tag then a fairly reliable way of running javascript after the entire page has loaded is placing your javascript in <script> tags at the very bottom of the page.
It's not my preferred way of handling this issue, but if using the onload event is not working quite right for you this would be a good next step.
what is the best way to load my script without causing any trouble with other scripts or frameworks
The things you need to consider are, are you..
polluting the namespace
obstructing another script
The second point is easy to work around, .addEventListener supports adding many listeners for the same event which won't overwrite each other. There are two different events you might be interested in, 'load' on window and 'DOMContentLoaded' on document. You can always put a <script> at the very end of <body> but I personally don't like doing this because you're mixing HTML with JavaScript or changing the DOM tree.
For the first point, this can be done via the use of anonymous functions and, should you need to occupy the namespace, keeping all of your variables and methods inside one object to minimise your impact.
Putting all this together you may end up with something like this
var myObjectName = { // everything in one object in global namespace
domain: {
function1: function () { /*...*/ },
function2: function () { /*...*/ },
function3: function () { /*...*/ }
}
};
(function (fn) {
if (window.addEventListener) {
window.addEventListener('load', fn, false);
}
else if (window.attachEvent) { // Consider if support for `.attachEvent` is really necessary, people need to move on.
window.attachEvent('onload', fn);
}
})(function () { // pass your function anonymously
myObjectName.domain.function1();
myObjectName.domain.function2(param1, param2);
myObjectName.domain.function3(1);
});
It's for a backbone application,
I'm using Jquery html() function to insert my views templates into the layout everywhere, and I would like to be able to trigger an event each time the html() function of jQuery is called to check the html of the page.
Is there a way to do that ?
( Like App.on('html', blablabla...); )
Thank you !
As Marc B suggested DOM MutationEvents is available on some browsers (not many). By default jQuery does not fire any event when using html, but you can define your own behaviour for this, for example:
(function($) {
var html_ref = $.fn.html;
$.fn.extend({
html : function() {
$(document).trigger( 'html_change' );
return html_ref.apply(this, arguments);
}
});
})($);
It should work, didn't test it though. You can use the same with .text method. Now you can simply use:
$(document).bind( 'html_change', function() {
// Hurray! Html changed!
});
That's the idea, use it as you wish.
AFAIK, the jQuery html() method doesn't fire any subscribable events per se, but you could probably roll your own implementation of a simple Observer pattern. I use this across a large number of projects and it provides a great, clean, lightweight way to encapsulate arbitrary event handling across loosely-coupled modules.
However, this is presuming that you have programmatic control over every time the html() method is called - if not, then this would be more difficult, as there is no callback function to hook into.
Regarding this SO article and this SO article. I looked at these after noticing my web app does not fire in IE8...I don't care about backward compatibility at the moment but if it's one line of code why not? Anyways the other issue I was having is the onload event waits for all the content to load...so the user has no controls if he/she is waiting for images to download. This led me to believe that I should just use
<script type='text/javascript'>my_initialize_function()</script>
placed in the html where I want the page to initialize.
and say to bye to
window.onload = initializePage;
or
window.addEventListener('load',initialize_page);
or any similar.
My question is: Is this a valid approach to initializing one's page?
PS: I'm not using any libraries including JQuery...and obviously I would not try to initialize elements that have not been loaded yet.
No.
jQuery and similar libraries has an interesting approach. They simply capture different events in a crossbrowser manner while making it easier for the developer to use.
Let's consider the following code:
<script> document.getElementById('a').innerHTML='b'; </script>
<div id="a"></div>
It may or may not work depending on whether the browser runs javascript when it finds it or only after the whole document has been built.
On the other hand, if you used the proper event mechanism but the document has already been built, your code will not be called.
jQuery unites both paradigms to get a seamless and better system. Something like so:
var foo = {
// holds list of callbacks to call when document starts
stack: [],
// document not ready yet
ready: false,
// add callback to be called when document is ready
add: function(callback){
foo.stack.push(callback);
if(foo.ready)foo.fire(); // document is ready, call fire
},
// fire callbacks (document IS ready)
fire: function(){
for(var i=0; i<foo.stack.length; i++)
foo.stack[i]();
foo.stack = [];
foo.ready = true; // document IS ready
}
};
// bind to browser events
window.onload = foo.fire; // TODO: you should use actual events
// example of use
foo.add(function(){
alert('Document is ready!');
});
You could use jQuery's $(document).ready() event. It fires after the DOM is complete, but before all images are loaded.
I'm looking for a good example where a framework/lib like jQuery removes the need for browser-specific code. Please note that I'm not looking for an example where jQuery makes things just easier or nice but specifically something where the same code wouldn't work for all common browsers.
As it'll be used for university stuff and just meant to be an example a very simple thing would be great (otherwise I'd probably use e.which vs e.keyCode)
Event handling is a good example, as it is so important for today's web applications.
element.addEventListener does not work in IE8 and below. These things are different in IE:
You have to use element.attachEvent and 'on<event>':
element.addEventListener('click', handler, false); // W3C
// vs.
element.attachEvent('onclick', handler); // IE
As IE's event model is a bit different, it does not support listing to events in the capture phase:
element.addEventListener('click', handler, true); // W3C
// vs
// not possible :( // IE
IE does not pass the event to the handler (it is available in via window.event):
function handler(event) {
event; // W3C
// vs
window.event; // IE
}
Inside the event handler, this does not refer to element the handler is bound to, but to window:
function handler() {
alert(window === this); // true // IE
}
Notice: This is only the case for event handlers bound via attachEvent. In inline event handlers and the ones assigned to the onclick property, this refers to the element.
Different properties of the event object (e.g. no event.stopPropagation):
event.stopPropgation(); // W3C
// vs.
event.cancelBubble = true; // IE
Except for the capturing, all these things can be dealt with by creating an appropriate wrapper function (what jQuery is basically doing). How such a method could look like can be found in my answer to this question.
You can find more information about these differences on quirksmode.org:
Advanced event registration models
Event properties
Event order
The this keyword
You can talk about jQuery's normalised event object, and in particular how it simplifies getting the event's target/sourceElement, eliminating the need for code like this:
function doSomething(e) {
var targ;
if (!e) var e = window.event;
if (e.target) targ = e.target;
else if (e.srcElement) targ = e.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
}
(From http://www.quirksmode.org/js/events_properties.html#target)
Handling AJAX requests, here's a sample function you'd need to get a XML http request object if you were supporting multiple browsers without using jQuery -
function getXHR () {
var request = null;
if (window.XMLHttpRequest) {
try {
request = new XMLHttpRequest();
} catch(e) {
request = null;
}
// Now try the ActiveX (IE) version
} else if (window.ActiveXObject) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
// Try the older ActiveX object for older versions of IE
} catch(e) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
request = null;
}
}
}
return request;
};
although all the examples stated above all are good examples but they are not part of everyones daily routine while working with JS.
The biggest advantage that I can see is how simple it is to use the selectors without me worrying about remembering the n of syntaxes and which browsers a particular method works and where not.
Yes I am talking about the native dom selectors like
getElementByid(id) => $("#id")
getElementByClass(class) => $(".class")
getElementByTagName("tagname") => $("tagName")
and also the fact that I dont need to worry about how many elements are being returned as part of the result set I can use some other api on the returned object instantly.
Also some of the dom manipulation code is something that irritates the hell out of me using native queries..
especially when i need to add new script tags into the dom you need to set the type etc. before inserting it into the dom with jquery all I need to do is use the html() method. and one more plus of the html() method is it will take care of which code implementation it should use that is whether it is possible to use innherHTML for better performance or if its a readonly control like some of those in IE append it to those etc..
then there are other things like others have noted how easy it is to deal with XHttp requests especially cross domain requests using jsonp with the SAME ORIGIN POLICY that is enforced by browsers..
Event handling being the other tricky thing with so many minor issues that you run into with every control.. just look at jquerys source code once and you will understand what I am talking of.