Tag Archives: jquery

jQuery onChange Event and Text Fields

18 Apr 2013

Seems like jQuery’s change event when bound to an input or textarea element doesn’t work like it should. What happens is that when those elements are blurred *then* onChange fires – completely not what we want to happen. According to the documentation this is actually by design. The only input elements that have an onChange event that behaves in a manner that makes sense are radios, checkboxs and select boxes. For anything else the onChange event will fire when the element loses focus!

...
$('input').change(function() {
  console.log('change, but only after the text input is blurred');
});

<input type=text">
...

There are a couple of events that you can use as an alternative, one is the “onInput” event. Curiously while this works in jQuery it is not part of jQuery’s API documentation (v1.9.1) – even though it works. The onInput event is part of the HTML 5 spec so my assumption is that jQuery’s support of it is here to stay, its just not part of the documentation for unknown reasons.

...
$('input').input(function() {
  console.log('behaves like a change event: as the text input element\'s value is changed the oninput event is fired');
});

<input type=text">
...

The caveat here – yes, you knew there would be one – is that since oninput is a member of the HTML5 spec that not every browser will support it. Thankfully IE9 and up supports it as does the crop of modern browsers.

For something more universal you might consider key bindings such as onKeydown or onKeyup, and then not worry about browser support.

...
$('input').keyup(function() {
  console.log('The text input element\'s value has changed');
});

<input type=text">
...

JQuery Mobile Checkboxes

31 Oct 2012

JQuery Mobile has some nice form element replacements that are more mobile device/touch-friendly than the way browsers currently present form elements. One of them is the visual replacement for a checkbox. To be clear the ugly checkbox is still there, its now layered underneath the JQM checkbox art.

The end results looks like this:

Click here to see a working example.

The docs IMO don’t quite tell you one critical thing – that certain tag attributes need to be identical in order for the framework to enhance the checkbox. If one of these is out of place you won’t get what you’re looking for.

Here’s how I implemented a checkbox today after trial and error:

...
  <style type="text/css">
    .checkboxWidth{
      width:53px;
    }
  </style>
...
  <fieldset data-role="controlgroup" class="checkboxWidth">
    <input type="checkbox" name="checkbox_value_0" id="checkbox_value_0" data-iconpos="notext" />
    <label for="checkbox_value_0" data="test"></label>
  </fieldset>
...

According to the docs the only thing to pay attention to is to ensure that the LABEL tag’s for attribute value is the same as the INPUT tag’s name value so that they are semantically linked together and displayed appropriately by the framework. There is one critical piece of information and that’s the INPUT tag’s id – it too must be the same value as the previous two attribute values. Omit that last bit and you don’t get any visual enhancement of the CHECKBOX element.

As far as getting the label removed all that is needed (as can be seen in the code sample above) is to add data-iconpos=”notext” to the input tag.

Lastly, I wanted to set the width so I added a class to the FIELDSET element and that was it, my check box was created and ready to go.

A handy tip is the use of the jQuery trigger method when inserting checkboxes into your document after it has loaded. Doing so ensures that you get the visual enhancement.

For example:

...
var newcbox = '<input type="checkbox" name="checkbox_value_0" id="checkbox_value_0" data-iconpos="notext" />';
    newcbox = '<label for="checkbox_value_0" data="test"></label>';

// add html to DOM and "trigger" jQuery visual enhancement
$(newcbox).appendTo('#contentWrapper').trigger('create');
...

Cache Busting Your Web Service Requests

29 Jun 2012

While working with web services you always need to keep in mind that web browsers cache files. if you web service request doesn’t change ever so slightly you will run into the issue of the web browser not pulling the new response but instead pulling the previously cached response. This is easily mitigated by adding a variable that itself is always unique – notice that i didn’t say “random” – random may work, sure, but “random” means that you run the risk of encountering that number again at some point. Yes, feel free to come up with a random 20 digit number and it may be for all intents and purposes appear to be “unique” but the risk of that number re-appearing is still there, however obscure it may be. As most web service calls are GET requests its as simple as adding a new name/value pair with a unique value to every request. The service ignores it and the browser takes note that the request is different than the previous request and so accepts the new response from the server.

Add the following somewhere within your code to get the unique value:

...
   var d = new Date();
   var unique = d.getTime();
...

That gives you a number similar to 1340979406482. That value is the number of milliseconds since January 1, 1970 so naturally it increments every millisecond. Ideally the getTime() method would accept a date as an argument and would then provide the milliseconds between Jan 1, 1970 and the specified date but since we don’t provide that value the current date is assumed. So, that number represents the number of milliseconds from that date in 1970 and the millisecond in time when the code was executed.

I have seen people blog this topic but include dividing the millisecond value by 1000 and rounding the result. Not a great idea. Dividing by 1000 and rounding the result shortens the length of the number by 3 digits. If you shorten it too much it won’t be useful. Already, by dividing by 1000 I’m not getting a number that updates every millisecond but instead a number that updates every second. I suppose it depends on your idea of “unique” and your specific application, but I’d rather have the guarantee of uniqueness than have something that is fuzzy like doing Math on the already guaranteed unique number… makes no sense.

As to applying this, here’s a jQuery example:

...
var d = new Date();
var unique = d.getTime();
var dataStr = 'name_value_pairs_here';
$.ajax({
  url:      	'/some_service_here/',
  data:      	dataStr + '&nocache=' + unique,
  contentType:	'application/x-www-form-urlencoded',
  dataType: 	'json',
  error:function(obj,status,xhr){
    // handle error here
  },
  success:function(obj,status,xhr){
    // handle response here
  }
});
...

And better yet – jQuery has such a thing built into it, simply set the cache attribute as in this example:

...
var dataStr = 'name_value_pairs_here';
$.ajax({
  url:      	'/some_service_here/',
  data:      	dataStr,
  cache:        false, // cache buster
  contentType:	'application/x-www-form-urlencoded',
  dataType: 	'json',
  error:function(obj,status,xhr){
    // handle error here
  },
  success:function(obj,status,xhr){
    // handle response here
  }
});
...

When using the “cache” attribute within the jQuery Ajax config object jQuery will append a new name/value pair to the GET request in the form of:

  1. _=[timestamp]

Where “[timestamp]” is a unique value.

The usefullness of using d.getTime() is still there outside of jQuery. For example any call to a file that you don’t want to be cached, such as web pages, JS files, CSS, etc.

Cross-Domain Ajax and XML with jQuery

07 May 2012

Did a fun little thing today – built a jQuery/PHP-based Cost of Living (COL) calculator for a city’s website (sorry, have to be vague on the client’s name) utilizing the data from www.coli.org.

Coli.org provides a service that allows interested parties to compare the cost of living across a range of criteria between two different locations within the Untied States.

Typically as in this case the destination location is the city that is providing the calculator. The intent is to allow potential new residents to compare their current cost of living to the destination city.

To start with you will need to query two of the services on page load – one to get the list of origin locations and another to get the list of destination locations (typically one or two locations – and if you want only one destination you could easily hard code the value of the city via a hidden form field or similar). Once you receive this data you will need to populate two select lists to allow the user to make their selections. With that data you will then query another web service to get the results. Further manipulation of the returned data on the client side with some math will get you percentages and adjusted income.

Approach

There are a couple of ways to approach this – server-side or client-side. As I’m a front-end guy my approach then was to do Ajax knowing that jQuery makes it trivial to do AJAX and parse XML. However, since this was going to be a cross-domain call a 100% client-side solution wouldn’t be possible. Instead, I would have to use something that wouldn’t be subject to a browser’s cross-domain policy – a proxy to act as the go-between for client and the web service. The server the site is hosted on supports PHP and so that dictated the tech to use. A little research revealed PHP’s cURL and a few lines of code later I had myself the proxy that I needed to accomplish the cross-domain service calls.

Proxy

One thing I noticed in the live examples that Coli.org provided is that the GUID was exposed which seemed unnecessary to me. So, instead of following their implementation examples verbatim I placed the GUID into the PHP proxy thereby keeping it secure. Of course, Coli.org might be mitigating GUID abuse by restricting calls to registered domains – I’ve no idea, but in any event, it seemed to be a better approach to not expose it if possible.

If you’re looking into using the Coli.org data by mirroring my approach you’ll be interested in the proxy, which I’ve provided below:

...

define ('HOSTNAME', 'http://www.coli.org/_clicktocomparews/service.asmx/');
$path = $_GET['ws_path'];
$url = HOSTNAME.$path;
$session = curl_init($url);
if ($_GET['ws_path']) {
     $getvars = '';
     while ($element = current($_GET)) {
          $getvars .= key($_GET).'='.$element.'&';
          next($_GET);
     }
     $getvars .= '&licenseeGUID={_YOUR_GUID_GOES_HERE_}';
     curl_setopt ($session, CURLOPT_POST, true);
     curl_setopt ($session, CURLOPT_POSTFIELDS, $getvars);
}

curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
$xml = curl_exec($session);
header("Content-Type: text/xml");
echo $xml;
curl_close($session)

...

All you need to do is to replace the string “_YOUR_GUID_GOES_HERE_” with the GUID that Coli.org gives you and as long as your server supports cURL your proxy is ready to be used. Note that the curly braces surrounding the numeric GUID is necessary.

AJAX & jQuery

Next is the AJAX. jQuery really makes it easy to do AJAX, here’s a stripped down version of my AJAX call to the Proxy:

...
function doAjax(wsPath,args){
   var dataStr = args ? 'ws_path=' + wsPath + '&' + args : 'ws_path=' + wsPath;
   $.ajax({
      url:      '/col_proxy.php',
      data:      dataStr + '&action=compute',
      dataType: 'xml',
      error:     function(xhr,status,err){
         //console.log(status + '\n' + err);
      },
      success:function(responseData,status,xhr){
         // console.log('success')
      });
}
...

So lets break this down, doAjax() expects two arguments to be passed to it. “wsPath” is the web service path, “args” are the arguments to be sent to the webservice. You would use the success callback to process the returned XML.

The web service paths for Coli.org’s web services are the following (as can be publicly seen here):

  • GetComparisonDetail
  • GetComparisonSummary
  • GetFromCommonPlaceTotal
  • GetFromLocations
  • GetPeriodInfo
  • GetSampleLocations
  • GetToCommonPlaceTotal
  • GetToLocations

Since I need the list of origin and destination locations first before any other web service can be used I call doAjax() via $(document).ready with the following placed at the end of my script:

...
$(document).ready(function(){
... 
... 
  // bottom of script
  (function init(){
    doAjax('GetFromLocations',null);
    doAjax('GetToLocations',null);
  })();
}); 

The “init” function is self-invoking and as you might have guessed, “GetFromLocations” is the web service for getting the list of origin locations, and “GetToLocations” provides the list of possible destination locations. These two services don’t require any extra attributes so the second argument to doAjax() is simply passed null.

The next task would be to call another webservice once the origin and destination cities have been selected by passing the data that repesents the city selections and the desired web service path. I didn’t use all of the webservices that Coli.org has availiable as they weren’t needed for my application. For my needs only two more calls were needed: “GetComparisonDetail” and “GetComparisonSummary”. In both of these cases the required name-value pairs to send along are:

  • fromCity
  • toCity

So then, knowing this and we would would pass the web service name as the first argument for doAjax() and construct the name/value pair string to be the value of the second argument. Something that in your own application may look similar to this:

...
// put together the "from" and to" name value pairs
// where "fromVal" is the origin city code and
// "toVal" is the destination city code
var args = 'fromCity=' + fromVal + '&toCity=' + toVal;

// specify the web service to use
var wServiceName = 'GetComparisonSummary';

// query the webservice
doAjax(wServiceName,args);
...

Processing XML

The result from a successful web service call will be XML. Coli.org unfortunately doesn’t provide XML samples so you’ll have to discover this on your own. What I did was to construct the entire web service call **sans proxy** directly in my browser’s address bar. The entire call, from www.colie.org… all the way to the GUID and any other necessary arguments – and then hit enter on my keyboard. If succesfull the XML response will appear in-browser.

Here’s an example of discovering the XML structure via a direct link in a browser:

http://www.coli.org/_clicktocomparews/service.asmx/GetFromLocations?licenseeGUID={_YOUR_GUID_HERE}

Here is a portion of the returned XML:

...

  
  T-Bone SteakFairbanksAK110.439.2655586592178789Q12011
T-Bone SteakWarsawIN19.79.2655586592178789Q12011
Ground BeefFairbanksAK13.83.0240502793296091Q12011
...

Once you know the XML structure the next few steps couldn’t be easier – in this case, each location is described as an XML node/tag called “Table”. Within this node are other nodes that themselves contain the specific values that you are looking for, such as:

  • Category_Name
  • Place_Name
  • State
  • Cost

In jQuery to find any node/tag within an XML doc simply use the “find()” method and you will get an array of all matching nodes. looking back at the doAjax() sample code we can see that the success callback assigns the returned XML to “responseData”. So then we have all the piecess to this rather small puzzle. To find all of the “Table” nodes within “responseData” our jQuery code would be:

...
var tableArray = $(responseData).find('Table');
...

This gives us all the “Table” nodes – but… we of course have to loop through each one looking for the specific values that we need and while a for loop would work here to iterate through each “Table” we should instead utilize chaining and jQuery’s built-in iterator, the “each()” method, and its callback:

...
$(responseData).find('Table').each(function(){
  // additional processing instructions here;
});
...

Pretty cool how chaining allows us to do multiple things simultaneously, right? This is a rather small chaining example too, but anyway, the next step is to find the desired values within each individual “Table” node. This is quite simple as well, see the next example:

...
$(responseData).find('Table').each(function(){
  var cname = $(this).find('Category_Name').text();
  var pname = $(this).find('Place_Name').text();
});
...

The $(this) statement in this context simply means “the current Table node” – then we look for the specific child node inside the current Table by again using the find() method. And on the end, we use the “text()” method to pull the text that is within the desired nodes. Here we are pulling the text for the “Category_Name” and “Place_Name” nodes which again are child nodes of “Table”.

THis is great, but this code doesn’t do us much good at this point as all we end up with are the Category and Place values of the very last Table in two variables. Instead we would actualy use this to populate the page with HTML for each individual Table. We can quickly print out the results to the page by appending html to a pre-existing div. As seen in the following example our existing div is called “tableResults”:

...
$(responseData).find('Table').each(function(){
  var currentRow =  '

Category: ' + $(this).find('Category_Name').text(); currentRow += ', Location: ' +$(this).find('Place_Name').text() + '

'; $('#tableResults').append(currentRow) }); ...

This will print out the Category and Location of each Table node within the mentioned pre-existing div called “tableResults”. Note that I’ve wrapped each set of Table data in a P tag. This gives us some automatic padding between lines which makes things a little more visually appealing. This sample is pretty basic, you’ll of course want to do more in your own application.

And… (drum roll)… thats about it. This was just a brief overview of the technical aspects of integrating into Coli.org’s web services via client-side technologies, not a complete functional example though with the code samples above – and complete working PHP-based proxy – you should be able to integrate with their web services quite easily or at the very least get a jump start on a similar project of your own.

Logo

All content © 2012-2017.