HTML event attributes:
<button id="myButton" onclick="someFunction()">Click me</button>
jQuery event methods:
$('#myButton').click(function () { alert('Hello'); });
Which way is more appropriate and gives better performance?
Which way is better?
The latter, or any of the other modern ways of doing it (addEventListener, etc. — e.g., you don't need jQuery for this, although you can use it for this if you like). onxyz-attribute-style handlers are generally a poor choice because:
They can only call global functions. In general, you want to avoid having global functions.
They force you to mix your code and HTML, rather than keeping them distinct. (This could be considered subjective.)
Note that this doesn't mean you need to have an id on every element; you can use the full power of CSS selectors to find elements (with jQuery, or with querySelector/querySelectorAll) in order to hook them up, they don't have to have ids.
Do be sure that your code runs after the element is known to exist. Generally, the known best practice is to put your script tags at the end of the document, just prior to the closing </body> element; at that point, all elements defined by the HTML above the script will exist.
For performance the vanilla JS methods are better, however this is usally not significant. The loss in performance occurs because the JQuery library has to be loaded in order to execute the JQuery event methods. If you only are using JQuery very little you could consider replacing it with vanilla JS for performance.
Also in your first case you are mixin JS and html which is often bad we the amount of JS is growing because your files will be harder to read. Usually it is best practice to seperate concern (put JS/CSS in seperate files from html)
Related
I have been struggling with choosing unobtrusive javascript over defining it within the html syntax. I want to convince my self to go the unobtrusive route, but I am having trouble getting past the issues listed below. Can you please help convince me :)
1) When you bind events unobtrusively, there is extra overhead on the client's machine to find that html element, where as when you do stuff, you don't have to iterate the DOM.
2) There is a lag between when events are bound using document.ready() (jquery) and when the page loads. This is more apparent on very large sites.
3) If you bind events (onclick etc) unobtrusively, there is no way of looking at the html code and knowing that there is an event bound to a particular class or id. This can become problematic when updating the markup and not realizing that you may be effecting javascript code. Is there a naming convention when defining css elements which are used to bind javascript events (i have seen ppl use js_className)
4) For a site, there are different pieces of javascript for different pages. For example Header.html contains a nav which triggers javascript events on all pages, where as homepage.html and searchPage.html contains elements that trigger javascript on their respective pages
sudo code example:
header.html
<script src="../myJS.js"></script>
<div>Header</div>
<ul>
<li>nav1</li><li>nav2</li>
</ul>
homepage.html
<#include header.html>
<div class="homepageDiv">some stuff</div>
searchpage.html
<#include header.html>
<div class="searchpageDiv">some other stuff</div>
myJS.js
$(document).ready(function(){
$("ul.li").bind("click",doSomething());
$(".homePageDiv").bind("click",doSomethingElse());
$(".searchPageDiv").bind("click",doSomethingSearchy());
});
In this case when you are on the searchPage it will still try to look for the "homepageDiv" which does not exist and fail. This will not effect the functionality but thats an additional unnecessary traversal. I could break this up into seperate javascript files, but then the browser has to download multiple files, and I can't just serve one file and have it cached for all pages.
What is the best way to use unobtrusive javascript so that I could easily maintain a ( pretty script heavy) website, so another developer is aware of scripts being bound to html elements when they are modifying my code. And serve the code so that the client's browser is not looking for elements which do not exist on a particular page (but may exist on others).
Thanks!
You are confused. Unobtrusive JavaScript is not just about defining event handlers in a program. It's a set of rules for writing JavaScript such that the script doesn't affect the functionality of other JavaScript on the same page. JavaScript is a dynamic language. Anyone can make changes to anything. Thus if two separate scripts on the same page both define a global variable add as follows, the last one to define it will win and affect the functionality of the first script.
// script 1
var add = function (a, b) {
return a + b;
};
// script 2
add = 5;
//script 1 again
add(2, 3); // error - add is a number, not a function
Now, to answer your question directly:
The extra overhead to find an element in JavaScript and attach an event listener to it is not a lot. You can use the new DOM method document.querySelector to find an element quickly and attach an event listener to it (it takes less than 1 ms to find the element).
If you want to attach your event listeners quickly, don't do it when your document content loads. Attach your event listeners at the end of the body section or directly after the part of your HTML code to which you wish to attach the event listener.
I don't see how altering the markup could affect the JavaScript in any manner. If you try to attach an event listener to an element that doesn't exist in JavaScript, it will silently fail or throw an exception. Either way, it really won't affect the functionality of the rest of the page. In addition, a HTML designer really doesn't need to know about the events attached any element. HTML is only supposed to be used for semantic markup; CSS is used for styling; and JavaScript is used for behavior. Don't mix up the three.
God has given us free will. Use it. JavaScript supports conditional execution. There are if statements. See if homePageDiv exists and only then attach an event listener to it.
Try:
$(document).ready(function () {
$("ul.li").bind("click",doSomething());
if (document.querySelector(".homePageDiv")) {
$(".homePageDiv").bind("click",doSomethingElse());
} else {
$(".searchPageDiv").bind("click",doSomethingSearchy());
}
});
Your question had very little to do with unobtrusive JavaScript. It showed a lack of research and understanding. Thus, I'm down voting it. Sorry.
Just because jQuery.ready() executes does not mean that the page is visible to the end user. This is a behaviour defined by browsers and these days there are really 2 events to take into consideration here as mootools puts it DomReady vs Load. When jQuery executes the ready method it's talking about the dom loading loaded however this doesn't mean the page is ready to be viewed by the user, external elements which as pictures and even style sheets etc may still be loading.
Any binding you do, even extremely inefficient ones will bind a lot faster than all the external resources being loaded by the browser so IMHO user should experience no difference between the page being displayed and functionality being made available.
As for finding binding on elements in your DOM. You are really just fearing that things will get lost. This has not really been my actual experience, more often than not in your JS you can check what page you are on and only add javascript for that page (as Aadit mentioned above). After that a quick find operation in your editor should help you find anything if stuff gets lost.
Keep in mind that under true MVC the functionality has to be separate from the presentation layer. This is exactly what OO javascript or unobtrusive javascript is about. You should be able to change your DOM without breaking the functionality of the page. Yes, if you change the css class and or element id on which you bind your JS will break, however the user will have no idea of this and the page will at least appear to work. However if this is a big concern you can use OO-Javascript and put div's or span's as placeholders in your dom and use these as markers to insert functionality or tell you that it exists, you can even use html comments. However, in my experience you know the behavior of your site and hence will always know that there is some JS there.
While I understand most of your concerns about useless traversals, I do think you are nickle and dime'ing it at this point if you are worried about 1 additional traversal. Previous to IE8 it used to be the case that traversing with the tag name and id was a lot faster than my selector but this is no longer true infact browsers have evolved to be much faster when using just the selectors:
$("a#myLink") - slowest.
$("a.myLink") - faster.
$("#Link") - fastest.
$(".myLink") - fastest.
In the link below you can see that as many as 34 thousand operations per second are being performed so I doubt speed is an issue.
You can use firebug to test the speed of each in the case of a very large dom.
In Summary:
a) Don't worry about losing js code there is always ctrl+f
b) There is no lag because dom ready does not mean the page is visible to start with.
Update
Fixed order of speed in operations based on the tests results from here
However keep in mind that performances of IE < 8 are really had if you don't specify the container (this used to be the rule, now it seems to be the exception to the rule).
In my answer to the following SO question: What does event binding mean?, I made a passing remark that the use of inline-JavaScript/Early-Binding to bind JavaScript Events was 'often misguided'
For example:
<input id="MyButton" type="button" value="clickme" onlick="Somefunction()" />
I was arguing for the 'late-binding' approach where there is no JavaScript referenced in the markup, which I understand to be established best-practice. However, the commenters asserted that there were occasions that necessitated its use, and I wondered what these might be.
Without engaging in a discussion on the relative merits of either, can anyone think of any circumstances that dictate that you use the (e.g.) onclick attribute over a late-binding approach.
commenters asserted that there were occasions that necessitated its use
I suppose I'm one of those commentors. What I actually said was that inline listeners "are a reasonable option in certain circumstances". I don't think there are any cases where it is "necessitated" (which I understand in this context to mean required).
Adding inline listeners is simply applying the same logic on the server to add listeners as would be applied on the client and has advantages in that:
Markup can be created and cached or used as static pages, listeners aren't added by every client over and over again when pages are downloaded
Issues related to delay between the element being available and the listener being added by DOMReady or onload functions or "bottom scripts" are completely removed
The vagaries of various "cross browser" DOMReady functions with onload fallback are removed - there is no opportunity for such functions to fail to add listeners if they aren't used
Of course this doesn't mean that all listeners should be added inline and that dynamic addition of listeners is rubbish, I'm just point out that it is a viable method that solves certain issues and is a perfectly reasonable solution in many cases.
If you believe "early binding" of listeners is good, then make it as early as possible - put them inline. :-)
PS. I also think I said I didn't like the use of "binding" in this context as listeners aren't bound to elements in any real sense. They are simply functions that are called when an element receives a related event. The only type of binding is that the listener's this keyword may be set to reference the related element (which is consistent in all browsers for inline listeners but not necessarily for those added later).
Reasons why onclick attributes are bad :
onclick="foo()"
Your passing in a string of code that get's evalled at runtime when the element is clicked. This is inefficient and uses the horrors of eval
Your forced to store the function foo in global scope thus polluting global scope with all your event handling logic.
I think many developers will do this either due to ignorance or lack of knowledge (which is, of course, common) and the remaining developers will do it because it's simply more convenient to use HTML-JS attributes than late binding, if you know that certain objects and functions are always loaded on every page and they will simply 'be there'.
I think this is especially true when said HTML comes from an AJAX callback. Take an example where an AJAX request comes back with an HTML response and that HTML is inserted into the page. Now the naive developer would think along these lines:
I have no idea what elements are inside that response HTML so I don't know what late bindings I need to add.
Perhaps I need to add them all just in case! Or write some parsing script that detects elements and binds to the ones I find?
But what if I need to bind to something that doesn't already exist? Time to write some long inline JavaScript!
All of this can be eliminated by using a kind of omnipresent binding that applies to all current and future elements on the page. In jQuery, the equivalent is live(). Instead of writing:
$('.foo').click(function(){...});
You could write:
$('.foo').live('click', function(){...});
Now, all elements with class name 'foo' will execute the function when clicked, including elements that don't currently exist. Very useful for dynamic AJAX interfaces.
You may know that already, but I'm just pointing out that anything JS attributes can do, pure JS can do better, and I'd consider that best practise.
I like the way jQuery wraps events when you use its "bind" method. However it's awkward to set up the bindings. Is there any way to combine the ease of html (e.g. onKeyPress="foo(event)" ) with jQuery's browser-independent event-handling goodness?
You mean like this?
function foo(event) {
alert(event.target);
}
$('.someSelector').keypress(foo);
Whether in HTML or javascript way you need to create a function foo, so maybe this is closer to what you were looking for.
I'm not sure what exactly you feel is awkward about jQuery's handler binding.
You probably don't want to do that.
You may want to read up on event delegation in JavaScript, which those jquery binding methods handle neatly for you.
Here's the highlights:
Event delegation has several benefits
to the performance of a web
application:
Fewer functions to manage. Takes up
less memory. Fewer ties between your
code and the DOM. Don’t need to worry
about removing event handlers when
changing the DOM via innerHTML.
You're better off using jQuery's binding helpers like $whatever.click(fn) etc.
Actually here is another question on SO that references the same article I did that may also help.
A good idea is to actually separate the two, to have your HTML clean and the js in a separate location. You can try shortcuts like $('a').click(...) instead of $('a').bind('click'...), but that's about it. Personally I like having the bindings separate from the markup.
is it OK to use the
$(document).ready(function ()
{
// some code
});
more than 1 time in the javascript code?
Yes, it is OK, jQuery will queue and merge them into a single handler called when the DOM is ready.
Sure it is ok. Sometimes you have no other option. especially when you have some included JS files with jQuery and some jQuery code in the page itself.
I find that having everything in one huge $(document).ready leads to messy code thats hard to read.
I often prefer to split it up and place a separate $(document).ready() for every part of the system that needs stuff added to it. This is especially nice for larger, modular, systems, where you dynamically add blocks of html, events and stuff.
For me it all boils down to what is most beneficial for you as a developer in a certain case.
Small system, where it's easy to know what's going on: one $(document).ready() in your script.
Large, modular, system: split it up as needed to have control of what's going on, and develop efficiently.
But as #Codesleuth commented: very often you don't need to put stuff inside the $(document).ready(), you only need it when you need to be sure the DOM is in a consistant and known state for heavy manipulation etc.
I see 2 main ways to set events in JavaScript:
Add an event directly inside the tag like this:
do foo
Set them by JavaScript like this:
<a id="bar" href="">do bar</a>
and add an event in a <script> section inside the <head> section or in an external JavaScript file, like that if you're using prototypeJS:
Event.observe(window, 'load', function() {
$('bar').observe('click', doBar);
}
I think the first method is easier to read and maintain (because the JavaScript action is directly bound to the link) but it's not so clean (because users can click on the link even if the page is not fully loaded, which may cause JavaScript errors in some cases).
The second method is cleaner (actions are added when the page is fully loaded) but it's more difficult to know that an action is linked to the tag.
Which method is the best?
A killer answer will be fully appreciated!
I think the first method is easier to read and maintain
I've found the opposite to be true. Bear in mind that sometimes more than one event handler will be bound to a given control.
Declaring all events in one central place helps to organize the actions taking place on the site. If you need to change something you don't have to search for all places making a call to a function, you simply have to change it in one place. When adding more elements that should have the same functionality you don't have to remember to add the handlers to them; instead, it's often enough to let them declare a class, or even not change them at all because they logically belong to a container element of which all child elements get wired to an action. From an actual code:
$$('#itemlist table th > a').invoke('observe', 'click', performSort);
This wired an event handler to all column headers in a table to make the table sortable. Imagine the effort to make all column headers sortable separately.
In my experience, there are two major points to this:
1) The most important thing is to be consistent. I don't think either of the two methods is necessarily easier to read, as long as you stick to it. I only get confused when both methods are used in a project (or even worse on the same page) because then I have to start searching for the calls and don't immediately know where to look.
2) The second kind, i.e. Event.observe() has advantages when the same or a very similar action is taken on multiple events because this becomes obvious when all those calls are in the same place. Also, as Konrad pointed out, in some cases this can be handled with a single call.
I believe the second method is generally preferred because it keeps information about action (i.e. the JavaScript) separate from the markup in the same way CSS separates presentation from markup.
I agree that this makes it a little more difficult to see what's happening in your page, but good tools like firebug will help you with this a lot. You'll also find much better IDE support available if you keep the mixing of HTML and Javascript to a minimum.
This approach really comes into its own as your project grows, and you find you want to attach the same javascript event to a bunch of different element types on many different pages. In that case, it becomes much easier to have a single pace which attaches events, rather than having to search many different HTML files to find where a particular function is called.
You can also use addEventListener (not in IE) / attachEvent (in IE).
Check out: http://www.quirksmode.org/js/events_advanced.html
These allow you to attach a function (or multiple functions) to an event on an existing DOM object. They also have the advantage of allowing un-attachment later.
In general, if you're using a serious amount of javascript, it can be useful to make your javascript readable, as opposed to your html. So you could say that onclick=X in the html is very clear, but this is both a lack of separation of the code -- another syntactic dependency between pieces -- and a case in which you have to read both the html and the javascript to understand the dynamic behavior of the page.
My personal preference is to use jQuery in external js files so the js is completely separate from the html. Javascript should be unobtrusive so inline (ie, the first example) is not really the best choice in my opinion. When looking at the html, the only sign that you are using js should be the script includes in the head.
An example of attaching (and handling) events might be something like this
var myObject = {
allLinkElements: null,
init: function()
{
// Set all the elements we need
myObject.setElements();
// Set event handlers for elements
myObject.setEventHandlers();
},
clickedLink: function()
{
// Handle the click event
alert('you clicked a link');
},
setElements: function()
{
// Find all <a> tags on the page
myObject.allLinkElements = $('a');
// Find other elements...
},
setEventHandlers: function()
{
// Loop through each link
myObject.allLinkElements.each(function(id)
{
// Assign the handler for the click event
$(this).click(myObject.clickedLink);
});
// Assign handlers for other elements...
}
}
// Wait for the DOM to be ready before initialising
$(document).ready(myObject.init);
I think this approach is useful if you want to keep all of your js organised, as you can use specific objects for tasks and everything is nicely contained.
Of course, the huge benefit of letting jQuery (or another well known library) do the hard work is that cross-browser support is (largely) taken care of which makes life much easier
Libraries like YUI and jQuery provide methods to add events only once the DOM is ready, which can be before window.onload. They also ensure that you can add multiple event handlers so that you can use scripts from different sources without the different event handlers overwriting each other.
So your practical choices are;
One. If your script is simple and the only one that will ever run on the page, create an init function like so:
window.onload = function () {
init();
}
function init() {
// actual function calls go here
doFoo();
}
Two. If you have many scripts or plan to mashup scripts from different sources, use a library and its onDOMReady method to safely add your event handlers