views:

116

answers:

4

I love jQuery but am running into a problem with larger site and multiple pages. My problem is that each page has unique requirements and I need to know the best way to tell jQuery which pages to activate certain things. For example, some forms need the Validator plug-in and some don't, some tables use DataTables plug-in and some don't, etc.

Now I guess I could build complex logic (switch statements) into my application JavaScript file that fire different actions depending on what page they are on, but that just seems smelly. What is the Best Practice here?

UPDATE: There have been lots of good ideas on this question but not quite what I'm looking for. Let me rephrase the question in a more general way.

Currently I am using Rails and its Prototype helpers to build my AJAX components, but I want to move to UJS. How do I tell jQuery which links/buttons to make AJAX and which to avoid? And, given that I can differentiate the that are supposed to have AJAX, how do I give each link its own parameters (method, update, etc.) like I could with the helpers?

I mean besides building a huge page of specific jQuery selectors targeting each individual link/button. :)

+2  A: 

jQuery plugins usually are activated upon DOM elements, for example:

$("#element").myPlugin();

If the element doesn't exist on the page, plugins usually behave safely by not activating the plugin.

If your plugin doesn't follow this structure I would suggest doing something like this:

if($("#element").length) $("#element").myPlugin();
Luca Matteis
The if condition will always evaluate to `true` as an object with property `length` and a value of `0` (among other properties) will be returned. Therefore you should check against the length property - `if($("#element").length) { .... } `
Russ Cam
Yes, but what if you have multiple pages with the same DOM elements? For example, my validator() plugin is set up like $('form').validate(), but sometimes I don't want it to act on all the forms on the page, only some of them. What do I do in this case?
RNHurt
@Richard: give the forms you want to validate a special class?
Christoph
@Christoph: Yes, I was just thinking that. Sometimes you have to bounce things off of other people before it makes sense in your own head. :)
RNHurt
+2  A: 

A good practice is to have code that is required by all pages in one file and to have specific javascript files for pages that require specific functionality. It sounds as though this is what you are doing anyway, so we have a good basis to build upon.

There are numerous ways in which you could build in what pages need what files, but remember that in normal circumstances, javascript files are cached by the browser such that those files need only downloading once.

In light of this comment

Yes, but what if you have multiple pages with the same DOM elements? For example, my validator() plugin is set up like $('form').validate(), but sometimes I don't want it to act on all the forms on the page, only some of them. What do I do in this case?

I suggest coming up with a convention by which to label elements common across pages that require certain jQuery plugins "attached" to them. For example, if you have a <form> element on a number of different pages that requires a validator() plugin, but there is more than one <form> element on any one particular page (and not all <form> elements should have the validator() plugin), then I suggest using a CSS class to distinguish the <form> elements that do need the plugin.

<!-- HTML -->

<!-- need to apply plugin to this -->    
<form class="validator"> ... </form>

<!-- but not to this -->
<form> ... </form>

<script type="text/javascript">
// jQuery Code (in a separate file)

$(function() {
    $('form.validator').validator();
});
</script>

That way, the plugin will be applied only to those <form> elements matching the selector.

EDIT:

I'm not sure how the helpers in rails work, but you can pass data to an event handler in jQuery using the data parameter of the bind() method for any data that is not directly part of an <a> element itself (such as an attribute like href). If some of the links require AJAX, then it may make sense to label those with a CSS class and store the URL in the href for the element. Then in your jQuery code that can be used on any pages that have links that make requests through AJAX, you could do something like

<a class="ajax-link" href="/get/someData.php">Data retrieved through AJAX</a>

<a href="/normalLink.php">Standard link with no AJAX</a>

<script type="text/javascript">
$('a.ajax-link').bind('click',ajaxRequest);

function ajaxRequest(e) {
    e.preventDefault(); 
    $.get(e.target.href, function(data) {
      $('#loadDiv').html(data);
    });
}
</script>

the link will work as per a normal link when a user has JavaScript disabled, but will make an AJAX request for data when JavaScript is enabled. I've used a named function here, ajaxRequest as it can make it easier to debug the script (and perhaps also reuse), but you could use an anonymous function if you prefer.

Russ Cam
Excellent insight. In my case there is a specific (very large) form that doesn't need the validator() plugin, so I will probably use a "NOT" selector to exclude it. Something like $('form:not(.no-validate)').validate(); ...
RNHurt
Certainly, if you need to apply the plugin to all but one `<form>` element then using a `:not` selector would perhaps be better than applying a CSS class to all the elements that do need the plugin. It may be slightly faster to just grab all the `<form>` elements and then use `filter()` to remove the one not needed rather than using a complex selector string.
Russ Cam
A: 

One thing I always do is add an id to the body tag of each page, where the id is the name of the page (<body id="contact">). That makes both javascript and css selection very easy.

For example:

<!-- HTML -->

<!-- need to apply plugin to these forms -->    
<form> ... </form> on the about.php page

<!-- but not to this -->
<form> ... </form> on the index.php page

<script type="text/javascript">
// jQuery Code (in a separate file)

$(function() {
    $('body#about form').validator();
});
</script>
Isley Aardvark
That's a pretty good way to perform different actions on different pages. However, I'm using Rails and I don't think it works like this. I could build a separate layout file for every type of page, but that would not be very DRY and would make it more difficult to cache the header.
RNHurt
A: 

You could pass key value pairs to your main JavaScript module informing which plugins should be imported. This technique is used by scriptaculous.

<script type="text/javascript" src="scriptaculous.js?load=effects,dragdrop">

Another approach is to define in server side which media files are needed for each form/page, so the html can be rendered automatically with links to the scripts that are going to be used. Django has a good implementation of this technique:

class CalendarWidget(forms.TextInput):
    class Media:
        css = {
            'all': ('pretty.css',)
        }
        js = ('animations.js', 'actions.js')
jbochi
This is a pretty cool technique, but I'm on Rails and as far as I know it doesn't have anything like this.
RNHurt