Sometimes we may have a generic function that could be triggered from a couple of different places. For example, let's say you have an unordered list with sortable li tags inside and clickable anchor tags that fire the same event to do whatever with the acted on list element.
New with jQuery 1.3 is the closest function, which will find the closest element matching the selector, including self. That means that in the scenario defined above we can have a function that looks like this to handle the event.
[js]function() {
var element = $(this).closest("li");
...
}[/js]
Pretty clean code and it will work as long as the event is triggered anywhere from the target li all the way down through any of its children.
This function is very similar the the parents function that we all know and love. That, of course, will not include the starting element in the search though.
More often than you might think. I have found that writing jQuery extensions is a clean and readable way to write not only plugins, but also application and page specific code. Recently there have been two scenarios where I found it appropriate to write some extensions.
The first is when you are using a plugin in multiple places and have a common way of invoking and interacting with it in your application. I was working with a modal window that was popped from multiple places, so I wrapped the modal plugin in my own extension that knew all the default settings and behaviors for the modal. That way my pages that used it could just invoke my plugin and not have to duplicate all the setup code. This is just an example to illustrate the technique, so please don't bother trying to execute it.
[js](function($) {
$.fn.extend({
openMyModal: function(options) {
//Merge the passed in and default settings, then call the plugin code.
this.popmodal($.extend($.logWorkoutResults.settings, options));
}
});
$.extend({logWorkoutResults: {
settings: {
someSetting: 'blah',
success: function() {
...
}
}
});
})(jQuery);[/js]
So the gist of what we're doing here is providing the common set of settings and function overrides in one place, but still allowing them to be overridden by the caller. The default settings and the passed in settings will be merged and then the plugin will be invoked. You would call it like this:
[js]$(".selector").openMyModal({ someSetting: 'blah blah' });[/js]
Pretty clean and easy to use, yet still easy to override if a certain page needs to change things. You can really go crazy with this, too. Since it is your extension just using a plugin, you can add all sorts of additional functionality.
The other time that it makes a lot of sense to write your own is when you find yourself writing loose functions that take one or more DOM elements as a parameter, or even if your function selects some elements and acts on them. For example, let's look at this plain old function call.
[js]MyPage.myFunction($("div.somestuff"), { parm1: 'value' });[/js]
Instead of having a loose function that depends on being passed a jQuery element, just rewrite your function to extend jQuery and it gets a whole lot cleaner.
[js]$("div.somestuff").myFunction({parm1: 'value'});[/js]The inside of the function is easy to change, too. The old one would have looked like this:
[js]var MyPage = {
myFunction: function(elements, settings) {
elements.each(function() {
//do stuff with 'this'
});
}
};[/js]
But now it will just look like this:
[js](function($) {
$.fn.extend({
myFunction: function(options) {
this.each(function() {
//do something with 'this'
});
}
});
})(jQuery);[/js]
So the implementation code doesn't look that much different, but the calling is a whole lot cleaner and easier to follow. You can just include that extension at the top of your page's js file or where it makes sense.
If you are modifying the DOM on the fly, then you have probably noticed that any event handlers you attach don't work on elements added after page load. Let's say you are making an ajax request and your server is returning an html response that you replace a portion of your DOM with. All the normal event handlers that were applied at page load were not applied to these elements, since they didn't exist at that time, and so now you have a bunch of stuff on the screen that doesn't work.
Your first thought might be to re-add all the event handlers after the DOM update is complete. This would get the job done, but wouldn't be very efficient and would require the event attachment code to be in multiple places.
A better approach would be to use the "live" function introduced in jQuery 1.3 (or the livequery plugin if you are still on 1.2). Instead of applying directly to the element, it captures bubbled events and checks the target element to see if it matches what is being watched. That way, an event on any element that matches the live's selector will have the event fired. The call is basically the same, except you pass in the name type of event to watch for.
[js]$("a.link_to_watch").live('click', function() {});[/js]At the time of writing this post, the event types not supported are blur, focus, mouseover, mouseleave, change, and submit. If you need to attach to those types of events, you will need to use the livequery plugin instead.
If you find yourself with a table of alternating rows, first a visible summary row followed by a hidden detail row, then selecting the next table row is a piece of cake. You may need to do it for any number of reasons, too. For starters, you may want to only turn on the "expand" icon in the summary row if there is actually data in the detail row. We will assume that the detail row contains div.something if there is actually data there.
[js]$("table tr:visible").each(function() {
($(this).next().is(":has(div.something)") && $(this).find("img.expand").show());
});[/js]
That will select the visible rows in the table and iterate over them. It will check each one to see if it has the div we are looking for and if it does, it will show the expand image. We are taking advantage of the fact that there are all function calls and using short circuit evaluation of the conditional to not run the argument after the && if it fails. One of my favorite Stupid Javascript Tricks.
You may also want to actually show and hide the table when the expand icon is clicked. Also pretty easy to do.
[js]$("table tr img.expand").click(function() {
$(this).closest("tr").next().toggle();
});[/js]
That will toggle the table row following the one containing the clicked expand icon. Pretty straightforward stuff, thanks to jQuery.
This is an easy one that I have been asked about a number of times before. I always recommend that you wrap your content area in a containing div to separate it from other portions of the page. Namespacing your DOM will keep your javascript and css from bleeding over into other panels unexpectedly. Hence the div#content. So, to automatically select the first element in the form on your page, all you need is this:
[js]$("div#content form :input:visible:first").focus();[/js]If you want the first empty element, then you just need to change it to:[js]$("div#content form :input:visible:first[value='']").focus();[/js]You may be wondering why I did :input:visible instead of just :text, and that is because I want to get any input elements that are on the screen, not just text inputs. Using :input will return input, textarea, select, and button elements. Adding :visible will not only filter out visibly hidden forms, but also type='hidden' input elements as well.