Saturday, October 26, 2013

jQuery Tips

In this post we will be examining some jQuery functions as well as selectors and some JavaScript tips that may help you along the way in the wild wild west of JavaScript.

We will be looking at the following:
The input/:input selector.
$.inArray()
The parent/child selector >.
Find.
Id starts with selector.
Combining input and id starts with.
Ancestor Descendant / Parent Child selector.
Checkboxes.
Radio Buttons.

For our first example, we will start with this snippet of html:
<div id="container">
   <table class="theTable">
      <tr>
         <td>
            <input type="text" id="productId" name="productId" value="5" />
         </td>
      </tr>
   <table>
</div>

We will look at several different ways of getting the value of the textbox and storing the value in an array. First, input and :input.
var productIds = [];

$('.theTable input').each(function () {
   var id = $(this).prop('id');
   if (id == "productId") {
      var isInArray = $.inArray($(this).prop('value'),productIds);
      if (isInArray == -1) {
         productIds.push($(this).attr('value'));
      }
   }
});

$('.theTable :input').each(function () {
   var id = $(this).prop('id');
   if (id == "productId") {
      var isInArray = $.inArray($(this).prop('value'), productIds);
      if (isInArray == -1) {
         productIds.push($(this).attr('value'));
      }
   }
});

The input selector will only select items designated with <input /> tags. The :input selector will match inputs, textareas, select items, and buttons. However, in order to get a checkbox, you use :checkbox or [type="checkbox"].

The $.inArray() function takes as arguments a value and an array. If the value is not found in the array, -1 is returned, if the value is found, the index of that item is returned.
And now a gotcha....
$('.theTable > input').each(function () {
   var id = $(this).prop('id');
   if (id == "productId") {
      var isInArray = $.inArray($(this).prop('value'), productIds);
      if (isInArray == -1) {
         productIds.push($(this).attr('value'));
      }
   }
});

Note the subtle change in the selector, the addition of the greater than symbol. The greater than symbol is a child selector, it will only select the direct children of an element. Given the html above, nothing will be found as the table element does not have any direct children that are inputs. More further down the post.

Our last example in this scenario is using the find() function. Find takes a selector, element, or an object and searches for descendants (down the DOM, only one level). Here we are passing in a selector for inputs.
$('.theTable').find('input').each(function () {
   var id = $(this).prop('id');
   if (id == "productId") {
      var isInArray = $.inArray($(this).prop('value'), productIds);
      if (isInArray == -1) {
         productIds.push($(this).attr('value'));
      }
   }
});

The id starts with selector is very helpful. It's great when id's are built dynamically and you need to rip through many inputs to get the value.
$('[id^="product"]').each(function () {
   productIds.push($(this).val());
});

Of course you can combine selectors for more precision.
$('input:text[id^="product"]').change(function () {
   productIds.push($(this).val());
});

There is a gotcha above when checking if the key is in the array. When debugging, you may peek into the object and see values but you call length on the array, the length will be....0. This is because the length property only tracks properties that have numeric indexes. In our example above, we are using strings.

Alright let's go more in depth with ancestor/descendant and parent/child selectors. Honestly, I find ancestor/descendant more useful. For the follow examples, we will be using this snippet of html:
<div id="container">
   <table id="tbl" class="theTable">
      <tr>
         <td>
            <input type="text" id="productId-1" name="productId-1" value="5" />
         </td>
         <td>
            <input type="text" id="quantity-1" name="quantity-1" value="10" />
         </td>
         <td>
            <input type="text" id="shipDate-1" name="shipDate-1" value="Now" />
         </td>
      </tr>
      <tr>
         <td>
            <input type="text" id="productId-2" name="productId-2" value="10" />
         </td>
         <td>
            <input type="text" id="quantity-2" name="quantity-2" value="15" />
         </td>
         <td>
            <input type="text" id="shipDate-2" name="shipDate-2" value="Later" />
         </td>
      </tr>
   </table>
</div>
What you really have to respect and like about jQuery is the flexibility that it gives. The following examples below are several ways of DOM manipulation and I am sure there are many more.
// this works as long as product id is the first td in each tr
// and remember, find only travels down one level
var tableRows = $('.theTable tr');
$.each(tableRows, function() {
   console.log("selector: $('.theTable tr') " + $(this).find('input').prop('id'));
});

Result:
selector: $('.theTable tr') productId-1
selector: $('.theTable tr') productId-2

// this gets all the td's directly
var tds = $('.theTable tr td');
$.each(tds, function() {
   console.log("selector: $('.theTable tr td') " + $(this).find('input').prop('id'));
});

Result:
selector: $('.theTable tr td') productId-1
selector: $('.theTable tr td') quantity-1
selector: $('.theTable tr td') shipDate-1
selector: $('.theTable tr td') productId-2
selector: $('.theTable tr td') quantity-2
selector: $('.theTable tr td') shipDate-2

// this will get all td's for each tr
var tdsEachTr = $('.theTable tr').children();
$.each(tdsEachTr, function() {
   console.log("selector: $('.theTable tr').children() " + $(this).find('input').prop('id'));
});

Result:
selector: $('.theTable tr').children() productId-1
selector: $('.theTable tr').children() quantity-1
selector: $('.theTable tr').children() shipDate-1
selector: $('.theTable tr').children() productId-2
selector: $('.theTable tr').children() quantity-2
selector: $('.theTable tr').children() shipDate-2

// this will get all td's for each tr as well
var tdEachTr2 = $('tr').children();
$.each(tdEachTr2, function() {
   console.log("selector: $('tr').children() " + $(this).find('input').prop('id'));
});

Result:
selector: $('tr').children() productId-1 
selector: $('tr').children() quantity-1 
selector: $('tr').children() shipDate-1
selector: $('tr').children() productId-2 
selector: $('tr').children() quantity-2
selector: $('tr').children() shipDate-2

// get all td's directly
var tdsDirect = $('td > td');
$.each(tdsDirect, function() {
   console.log("selector: $('tr > td') " + $(this).find('input').prop('id'));
});

Result:
selector: $('tr > td') productId-1
selector: $('tr > td') quantity-1
selector: $('tr > td') shipDate-1
selector: $('tr > td') productId-2
selector: $('tr > td') quantity-2
selector: $('tr > td') shipDate-2
For the final segment, we will be working with checkboxes and radio buttons. Checkboxes and radio buttons are similar so the code is pretty much interchangeable. The following snippet of html will be used to illustrate:
<div id="checkboxes">
   <input type="checkbox" id="ckBox1" name="ckBox1" value="First Check" /> First Check
   <input type="checkbox" id="ckBox2" name="ckBox2" value="Second Check" /> Second Check
   <input type="checkbox" id="ckBox3" name="ckBox3" value="Third Check" /> Third Check
   <input type="checkbox" id="ckBox4" name="ckBox4" value="Fourth Check" /> Fourth Check
</div>
<div id="radiobuttons">
   <input type="radio" id="rdo1" name="rdo1" class="rdo" value="First Radio" />First Radio
   <input type="radio" id="rdo2" name="rdo2" class="rdo" value="Second Radio" />Second Radio
   <input type="radio" id="rdo3" name="rdo3" class="rdo" value="Third Radio" />Third Radio
   <input type="radio" id="rdo4" name="rdo4" class="rdo" value="Fourth Radio" />Fourth Radio
</div>
<div>
   <input type="submit" id="btnSubmit" name="btnSubmit" value="Submit" />
</div>

// get all checkboxes
var checkboxes = $('#checkboxes input:checkbox');
$.each(checkboxes, function() {
   console.log("selector: $('#checkboxes input:checkbox')");
   console.log("checkbox id: " + $(this).prop('id'));
   console.log("checkbox value: " + $(this).val());
});

Result:
selector: $('#checkboxes input:checkbox') 
checkbox id: ckBox1 
checkbox value: First Check

selector: $('#checkboxes input:checkbox') 
checkbox id: ckBox2
checkbox value: Second Check

selector: $('#checkboxes input:checkbox') 
checkbox id: ckBox3 
checkbox value: Third Check

selector: $('#checkboxes input:checkbox') 
checkbox id: ckBox4 
checkbox value: Fourth Check

// inspect checkbox on change
$('[id^="ckBox"]').change(function() {
   var isChecked = $(this).is(':checked');
   if (isChecked == true) {
      console.log("checkbox id: " + $(this).prop('id'));
      console.log("checkbox value: " + $(this).val());
   }
});

// get all checked checkboxes on submit
$('#btnSubmit').click(function(e) {
   e.preventDefault();
   $('#checkboxes input:checkbox:checked').each(function() {
      console.log("selector: $('#checkboxes input:checkbox:checked')");
      console.log("checkbox id: " + $(this).prop('id'));
      console.log("checkbox value: " + $(this).val());
   });

   // or
   $('#checkboxes').children('input:checked').each(function() {

   });

   // or
   $('#checkboxes input[type=checkbox]:checked').each(function() {

   });
});

// get all radio buttons
var radiobuttons = $('#radiobuttons input:radio');
$.each(radiobuttons, function() {
   console.log("selector: $('#radiobuttons input:radio')");
   console.log("radio id: " + $(this).prop('id'));
   console.log("radio value: " + $(this).val());
});

Result:
selector: $('#radiobuttons input:radio') 
radio id: rdo1
radio value: First Radio

selector: $('#radiobuttons input:radio') 
radio id: rdo2
radio value: Second Radio

selector: $('#radiobuttons input:radio') 
radio id: rdo3
radio value: Third Radio

selector: $('#radiobuttons input:radio') 
radio id: rdo4
radio value: Fourth Radio

// inspect radio button on change
$('[id^="rdo"]').change(function() {
   var result = $(this).is(':checked');
   if (result == true) {
      console.log("radio id: " + $(this).prop('id'));
      console.log("radio value: " + $(this).val());
   }
});

// get all selected radio buttons on submit
$('#btnSubmit').click(function(e) {
   e.preventDefault();
   $('#radiobuttons input:radio:checked').each(function() {
      console.log("selector: $('#radiobuttons input:radio:checked')");
      console.log("radio id: " + $(this).prop('id'));
      console.log("radio value: " + $(this).val());
   });

   // or
   $('#radiobuttons').children('input:checked').each(function() {

   });

   // or
   $('#radiobuttons input[type=radio]:checked').each(function() {

   });
});