जगदीश खोलिया: JQuery Concepts

Wednesday, September 21, 2011

JQuery Concepts

How do I select an item using class or ID?

This code selects an element with an ID of "myDivId". Since IDs are unique, this expression always selects either zero or one elements depending upon whether or not an element with the specified ID exists.
 $('#myDivId')
This code selects an element with a class of "myCssClass". Since any number of elements can have the same class, this expression will select any number of elements.
 $('.myCssClass')
A jQuery object containing the selected element can be assigned to a JavaScript variable like normal:
 var myDivElement = $('#myDivId');
Usually, elements in a jQuery object are acted on by other jQuery functions:
 var myValue = $('#myDivId').val();    // get the value of a form input
 
 $('#myDivId').val("hello world");     // set the value of a form input

How do I select elements when I already have a DOM element?

If you have a variable containing a DOM element, and want to select elements related to that DOM element, simply wrap it in a jQuery object.
 var myDomElement = document.getElementById('foo'); // a plain DOM element
 $(myDomElement).find('a'); // finds all anchors inside the DOM element
Many people try to concatenate a DOM element or jQuery object with a CSS selector, like so:
 $(myDomElement + '.bar'); // WRONG! equivalent to $("[object HTMLElement].bar")
This is wrong. You cannot concatenate strings to objects.

How do I test whether an element has a particular class?

hasClass (added in version 1.2) handles this common use case:
 $("div").click(function(){
   if ( $(this).hasClass("protected") )
     $(this)
       .animate({ left: -10 })
       .animate({ left: 10 })
       .animate({ left: -10 })
       .animate({ left: 10 })
       .animate({ left: 0 });
 });
You can also use the is() method along with an appropriate selector for more advanced matching:
 if ( $('#myDiv').is('.pretty.awesome') )
   $('#myDiv').show();
Note that this method allows you to test for other things as well. For example, you can test whether an element is hidden (by using the custom :hidden selector):
 if ( $('#myDiv').is(':hidden') )
   $('#myDiv').show();

Use the length property of the jQuery collection returned by your selector:
 if ( $('#myDiv').length )
   $('#myDiv').show();
Note that it isn't always necessary to test whether an element exists. The following code will show the element if it exists, and do nothing (with no errors) if it does not:
 $('#myDiv').show();

How do I determine the state of a toggled element?

You can determine whether an element is collapsed or not by using the :visible and :hidden selectors.
 var isVisible = $('#myDiv').is(':visible');
 var isHidden = $('#myDiv').is(':hidden');
If you're simply acting on an element based on its visibility, just include ":visible" or ":hidden" in the selector expression. For example:
 $('#myDiv:visible').animate({left: '+=200px'}, 'slow');

How do I select an element by an ID that has characters used in CSS notation?

Because jQuery uses CSS syntax for selecting elements, some characters are interpreted as CSS notation. For example, ID attributes, after an initial letter (a-z or A-Z), may also use periods and colons, in addition to letters, numbers, hyphens, and underscores (see W3C Basic HTML Data Types). The colon (":") and period (".") are problematic within the context of a jQuery selector because they indicate a pseudo-class and class, respectively.
In order to tell jQuery to treat these characters literally rather than as CSS notation, they must be "escaped" by placing two backslashes in front of them.
 // Does not work
 $("#some:id")
 
 // Works!
 $("#some\\:id")
 // Does not work
 $("#some.id")
 
 // Works!
 $("#some\\.id")
The following function takes care of escaping these characters and places a "#" at the beginning of the ID string:
 function jq(myid) { 
   return '#' + myid.replace(/(:|\.)/g,'\\$1');
 }

The function can be used like so:
 $( jq('some.id') )

How do I disable/enable a form element?

There are two ways to disable/enable form elements.
Set the 'disabled' attribute to true or false:
 // Disable #x
 $('#x').attr('disabled', true);
 // Enable #x
 $('#x').attr('disabled', false);
Add or remove the 'disabled' attribute:
 // Disable #x
 $("#x").attr('disabled', 'disabled');
 // Enable #x
 $("#x").removeAttr('disabled');
You can try an example of enabling/disabling with the following demo:

and here's the source code to the demo:
 <select id="x" style="width:200px;">
   <option>one</option>
   <option>two</option>
 </select>
 <input type="button" value="Disable" onclick="$('#x').attr('disabled','disabled')"/>
 <input type="button" value="Enable" onclick="$('#x').removeAttr('disabled')"/>

How do I check/uncheck a checkbox input or radio button?

There are two ways to check/uncheck a checkbox/radio button.
Set the 'checked' attribute to true or false.
 // Check #x
 $('#x').attr('checked', true);
 // Uncheck #x
 $('#x').attr('checked', false);
Add or remove the 'checked' attribute:
 // Check #x
 $("#x").attr('checked', 'checked');
 // Uncheck #x
 $("#x").removeAttr('checked');

and here's the source code to the demo:
 <label><input type="checkbox" id="c"/> I'll be checked/unchecked.</label>
 <input type="button" value="Check" onclick='$("#c").attr("checked","checked")'/>
 <input type="button" value="Uncheck" onclick='$("#c").removeAttr("checked")'/>

How do I get the text value of a selected option?

Select elements typically have two values that you want to access. First there's the value to be sent to the server, which is easy:
 $("#myselect").val();
 // => 1
The second is the text value of the select. For example, using the following select box:
 <select id="myselect">
   <option value="1">Mr</option>
   <option value="2">Mrs</option>
   <option value="3">Ms</option>
   <option value="4">Dr</option>
   <option value="5">Prof</option>
 </select>
If you wanted to get the string "Mr" if the first option was selected (instead of just "1"), you would do that in the following way:
 $("#myselect option:selected").text();
 // => "Mr"
You can see this in action in the following demo:

and here's the full source code to the demo:
 <select id="myselect">
   <option value="1">Mr</option>
   <option value="2">Mrs</option>
   <option value="3">Ms</option>
   <option value="4">Dr</option>
   <option value="5">Prof</option>
 </select>
 <input type="button" value="Get Value" onclick="alert($('#myselect').val())"/>
 <input type="button" value="Get Text Value" onclick="alert($('#myselect option:selected').text())"/>

How do I replace text from the 3rd element of a list of 10 items?

Either the :eq() selector or the .eq() method will allow you to select the proper item. However, to replace the text, you must get the value before you set it:
  // This doesn't work; text() returns a string, not the jQuery object
  $(this).find('li a').eq(2).text().replace('foo','bar');

  // This works
  var $thirdLink = $(this).find('li a').eq(2);
  var linkText = $thirdLink.text().replace('foo','bar');
  $thirdLink.text(linkText);
The first example just discards the modified text. The second example saves the modified text and then replaces the old text with the new modified text. Remember, .text() gets; .text("foo") sets.



How do I get and use the server response from an AJAX request?

The 'A' in AJAX stands for asynchronous. When invoking functions that have asynchronous behavior you must provide a callback function to capture the desired result. This is especially important with AJAX in the browser because when a remote request is made, it is indeterminate when (or even if) the response will be received.
The following snippet shows an example of making an AJAX call and alerting the response (or error):
 $.ajax({
     url: 'myPage.php',
     success: function(response) {
        alert(response);
     },
     error: function(xhr) {
        alert('Error!  Status = ' + xhr.status);
     }
 });
But how can the response be used in context of a function? Consider this flawed example where we try to update some status information on the page:
 function updateStatus() {
     var status;
     $.ajax({
         url: 'getStatus.php',
         success: function(response) {
             status = response;
         }
     });
     // update status element?  this will not work as expected
     $('#status').html(status);
 }
The code above does not work as desired due to the nature of asynchronous programming. The provided success handler is not invoked immediately, but rather at some time in the future when the response is received from the server. So when we use the 'status' variable immediately after the $.ajax call, its value is still undefined. The next snippet shows how we can rewrite this function to behave as desired:
 function updateStatus() {
     $.ajax({
         url: 'getStatus.php',
         success: function(response) {
             // update status element
             $('#status').html(response);
         }
     });
 }
But how can I return the server response from an AJAX call? Here again we show a flawed attempt. In this example we attempt to alert the http status code for the url of 'getStatus.php':
 //...
 alert(getUrlStatus('getStatus.php'));
 //...
 function getUrlStatus(url) {
     $.ajax({
         url: url,
         complete: function(xhr) {
             return xhr.status;
         }
     });
 }
The code above will not work because you cannot 'return' data from a function that is called asynchronously. Instead, it must be rewritten to use a callback:
 //...
 getUrlStatus('getStatus.php', function(status) {
     alert(status);
 });
 // ...
 function getUrlStatus(url, callback) {
     $.ajax({
         url: url,
         complete: function(xhr) {
             callback(xhr.status);
         }
     });
 }

How do I pull a native DOM element from a jQuery object?

A jQuery object is an array-like wrapper around one or more DOM elements. To get a reference to the actual DOM elements (instead of the jQuery object), you have two options. The first (and fastest) method is to use array notation:
 $('#foo')[0]; // equivalent to document.getElementById('foo')
The second method is to use the get function:
 $('#foo').get(0); // identical to above, only slower
You can also call get without any arguments to retrieve a true array of DOM elements.

Why do ... ?


Why do my events stop working after an AJAX request?

Frequently, when you've added a click (or other event) handler to all links using $('a').click(fn), you'll find that the events no longer work after you've loaded new content into a page using an AJAX request.
When you call $('a'), it returns all the links on the page at the time it was called, and .click(fn) adds your handler to only those elements. When new links are added, they are not affected.
You have two ways of handling this:

Using event delegation

Event delegation is a technique that exploits event bubbling to capture events on elements anywhere in the DOM.
As of jQuery 1.3, you can use the live and die methods for event delegation with a subset of event types. As of jQuery 1.4, you can use these methods (along with delegate and undelegate starting in 1.4.2) for event delegation with pretty much any event type.
For earlier versions of jQuery, take a look at the Live Query plugin by Brandon Aaron. You may also manually handle event delegation by binding to a common container and listening for events from there. For example:
 $('#mydiv').click(function(e){
    if( $(e.target).is('a') )
       fn.call(e.target,e);
 });
 $('#mydiv').load('my.html');
This example will handle clicks on any <a> element within #mydiv, even if they do not exist yet when the click handler is added.

Using event rebinding

This method requires you to call the bind method on new elements as they are added. For example:
 $('a').click(fn);
 $('#mydiv').load('my.html',function(){
   $('#mydiv a').click(fn);
 });
Beware! As of jQuery 1.4.2, binding the same handler to the same element multiple times will cause it to execute more than once. This differs from previous versions of jQuery as well as the DOM 2 Events spec (which normally ignores duplicate event handlers).

Why doesn't an event work on a new element I've created?

As explained in the previous question about AJAX, events are bound only to elements that exist at the time when you issue your initial jQuery call. When you create a new element, you must bind the event to it separately, or use event delegation.

Why do animations set the display style to block?

Only block-level elements can have a custom width or height. When you do an animation on an element that animates the height or width (such as show, hide, slideUp, or slideDown), the display CSS property will be set to 'block' for the duration of the animation. The display property will be reverted to its original value after the animation completes. (This does not work properly for inline-block elements.)
There are two common workarounds:
If you want the element to stay inline, but you just want it to animate in or out, you can use the fadeIn or fadeOut animations instead (which only affect the opacity of an element).
 // Instead of this:
 $("span").show("slow");
 
 // do this:
 $("span").fadeIn("slow");
The other option is to use a block-level element, but to add a float such that it appears to stay inline with the rest of the content around it. The result might looks something like this:
 // A floated block element
 <div style="float:left;">...</div>
 
 // Your code:
 $("div").show("slow");

No comments: