High end javascript application and Event - javascript

I tried to have a title that match the context I want my answer for.
I explain myself.
After writting thousand of javascript lines of code (with or without jquery) I finally asked myself if I'm doing it wrong.
Mainly thanks to jQuery adding event/delegating to an element is very easy, but all the example are quiet "easy"; meaning it doesn't take into account that you have a page full of list, button, dynamic things...
So my questions, is:
Once again for a complexe website, with hundred of dynamic pages, ajax navigation, millions of user...
Is there any advantage (beside simplicity) to place a global listener (body or whatever) and let all events bubble up ?
When I look at angular it seems they listen 'ng-click' globally, but I can missthink.
Contexte
<body>
<div>
<ul>
<li><span click="myFunc"></span></li>
</ul>
</div>
</body>
-
Sidenot: I'm using jQuery for presentation/simplicity, but it's global question about js
// Standard binding example
$('span').on('click', func)
vs
// Standard delegation example
$('ul').on('click', 'span', func)
vs
// Dispatcher for 'block'
$('ul').on('click', localDispatcher)
vs
// One (and only one) Global Listener for all click
$(body).on('click', globalDispatcher)
With this example, the 3 first one need to be placed manually, on a dedicated js for this page.
Vs
The last one is just a simple event listener, that will use click attribute to dispatch and can be set just once and for all.
I find the last one very cool, because it remove all the hassle to manage click/selector/dom management. Also I don't have to .off every listener when I change page (ajax navigation).
But I wonder about performance or maybe another problems I can think about (that's why I ask the question :D).
I really wonder how "big web startup" manage this kind of Javascript.
Thanks :)

I'd say no to a global listener since it will make things incredibly confusing and frustrating to organize once the app grows into a considerable size.
Also the performance should be really bad since you are involving elements that will be ignored otherwise.
So stick with element or block level event delegation if you want to feel good about yourself in the future!

Related

Is $.empty() enough for big ajaxy apps?

Been working on an App and since it's getting a bit too big I've thinking of ways to improve memory management since the app runs mostly on Javascipt. So every time a navigation item is clicked I would call the jquery empty then show the html via ajax. ex:
//$.ajaxSetup(); called before this
//$this is the attached element
$.ajax({success:function(data){
$this.empty().html(data.output).fadeIn(400);
//more javascript stuff like loading tinymce or jquery ui
}});
is this enough to prevent memory leaks? I'm not entirely sure what empty does but I'm assuming it removes all DOM elements within that div along with any other objects and events? btw. You can find the app here http://webproposalgenerator.com/ and http://webproposalgenerator.com/demo.
any tips on improving the performance/security or any feedback at all would be greatly appreciated.
$.fn.empty should be enough, it deletes all data and events associated to the elements and then deletes the elements. It also calls .widget("destroy") on all jquery-ui widget.js based widgets that are defined on those elements.
It is also important to note that jquery's $.fn.html method calls $.fn.empty() on the given element before appending html, therefore, if you are using $.fn.html, you don't have to call $.fn.empty
actually my guess was that .html implies .empty anyway, also I'm not sure that's true. for the perforamnce part: according to jqfundamentals excelent book it is a recommanded best practice to add content while the element is in .detach() from the DOM. tried to lock at the code for advice but didn't find it. nice site btw

unobtrusive Javascript, should I use it? what is the best way to manage and organize events? how do I prevent inefficiencies?

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).

JQuery/Javascript process question

I've inherited someone else's project and am building a work flow diagram for it. Without getting into too many details the person who left was the only person in the web department with advanced programming skills (most people there do production work and some HTML/CSS stuff). The project I inherited was developed in CodeIgniter and leans heavily on JQuery, AJAX and JSON. The flow is a bit confusing hence my outlining it. (I'm getting to the question, bear with me)
Anyway, the manager of this department, let's refer to him as The Tool, won't allow his people to learn any of this stuff. He asked me the other day how it was coming and I said fine except I can't find where one variable is being set, the original developer is using jquery to call some form value to set a path to files (he using #id.val()) but I can't find #id anywhere in the code. The manager replies, eh, I thought you were the PHP guru. As I said, we shall refer to him as The Tool.
Anyway, to stick it to him somewhat I've decided to share these flow pages with people in his group, make them very descriptive and hopefully educational. I'm explaining how when a change is made from a select menu jquery/javascript recognizes that change and fires off related code in JS.
Then it dawned on me that I really didn't know how JS/JQ knew the change had been made. I know the code ($("#id").change()...I have an AppleScript background and in that language there's an idle command, you basically can have a script sit in the background observing and waiting for X to happen (say the user launches Photoshop) and when that event happens the rest of the code is run. Does JS do something similar?
This code:
$("#id").change()
tells jQuery (the "$") to find the element whose "id" value is (in this case) "id", and then to trigger a "change" event on the element. That will cause event handlers registered to look for such events on that element to be run. That's the basis of just about everything you do with JavaScript in a browser: responses to events.
Somewhere, there's probably one of these:
$("#id").change(function() { ... })
$("#id").live('change', function() { ... })
$("#id").bind('change', function() { ... })
There are many ways for the element to have been identified for setting up the event handlers, however, so it may be tricky to find.
In newer versions of chrome you can inspect a html element, and this will also show 'classic' events that are bound to it.
Increasingly good developers are moving to event delegates such as:
http://developer.yahoo.com/yui/3/event/#delegate
Which will likely not show up in Chrome, but should be readable from the code.

Are there any drawbacks to class-based Javascript injection?

A phenomena I'm seeing more and more of is Javascript code that is tied to a particular element on a particular page, rather than being tied to kinds of elements or UI patterns.
For example, say we had a couple of animated menus on a page:
<ul id="top-navigation">
...
</ul>
<!-- ... -->
<ul id="product-list">
...
</ul>
These two menus might exist on the same page or on different pages, and some pages mightn't have any menus.
I'll often see Javascript code like this (for these examples, I'm using jQuery):
$(document).ready(function() {
$('ul#top-navigation').dropdownMenu();
$('ul#product-selector').dropdownMenu();
});
Notice the problem?
The Javascript is tightly coupled to particular instances of a UI pattern rather than the UI pattern itself.
Now wouldn't it be so much simpler (and cleaner) to do this instead? -
$(document).ready(function() {
$('ul.dropdown-menu').dropdownMenu();
});
Then we can put the 'dropdown-menu' class on our lists like so:
<ul id="top-navigation" class="dropdown-menu">
...
</ul>
<!-- ... -->
<ul id="product-list" class="dropdown-menu">
...
</ul>
This way of doing things would have the following benefits:
Simpler Javascript - we only need to attach once to the class.
We avoid looking for specific instances that mightn't exist on a given page.
If we remove an element, we don't need to hunt through the Javascript to find the attach code for that element.
I believe techniques similar to this were pioneered by certain articles on alistapart.com.
I'm amazed these simple techniques still haven't gained widespread adoption, and I still see 'best-practice' code-samples and Javascript frameworks referring directly to UI instances rather than UI patterns.
Is there any reason for this? Is there some big disadvantage to the technique I just described that I'm unaware of?
First of all I agree with you that using the class approach is better, in general.
But I don't think I'd go so far as to say it's less coupling of the code to the UI. If you think about it, if the code assumes ID "foo" vs. class name "foo", you still have to know that when working with the UI. There's still a 'contract' between them -- whether you meet it through ID or class is not really different.
One disadvantage to using the class approach I'd imagine is speed -- it should be faster to find a particular element by ID than find potentially multiple elements by class. The difference is probably completely negligible though.
But, in the case where your code is designed to attach multiple behaviors, as in your two-dropdown example, using class certainly makes more sense. That is less coupling since your code is a bit more generalized, and your UI more likely to be customizable w/o changing the code.
One thing I'd change in both of your examples... why have the UL in the selector? If the code knows it can only possibly work if the target is a UL, well, that's one thing -- but in that case, it'd be better to avoid the UL in the selector and let the code throw a meaningful error if the target is found not to be a UL, lest the page just do nothing without any indication as to why (e.g. because the UI put the ID/class on a OL).
So in other words, just "#foo" or ".foo" not "ul.foo", etc.
I should point out that in case someone thinks the UL somehow makes the selector more efficient, it doesn't, since selectors are evaluated from right to left.
Your approach is preferred.
The reason people do things in different ways is because they can and it still works.

What is the best way to add an event in JavaScript?

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

Categories