Mark Needham

Thoughts on Software Development

Archive for the ‘jQuery’ Category

jQuery: Collecting the results from a collection of asynchronous requests

with 5 comments

Liz and I recently spent some time building a pair stair to show how long ago people had paired with each other and one of the things we had to do was make AJAX requests to get the pairing data for each person and then collate it all to build the stair.

Pair stair

The original attempt to do this looked a bit like this:

var people = ["Marc", "Liz", "Ken", "Duncan", "Uday", "Mark", "Charles"];
 
var grid = [];
$.each(people, function(index, person) {
  $.getJSON('/git/pairs/' + person, function(data) {
    // parse data and create somethingCool
    grid.push(somethingCool);
  });  
});
 
// do something with grid

When we try to do something with grid it is of course empty because we’ve attempted to access it before all of the callbacks (which populate it) have returned.

Pedro Teixeira has a nice blog post which explains how to solve this problem in node.js and we can use the same pattern here.

We need to write our own looping mechanism which is able to determine when the last callback has returned.

This is done by creating a copy of the people array and then manually iterating through it using shift.

var people = ["Marc", "Liz", "Ken", "Duncan", "Uday", "Mark", "Charles"];
var peopleCopy = people.slice(0), grid = [];
(function getPairs() {
  var person = peopleCopy.shift();
 
  if(peopleCopy.length == 0) {
    // do something with grid
  } else {
    $.getJSON("/git/pairs" + person, function(data) {
      // parse data and create somethingCool
      grid.push(somethingCool);
      getPairs();		
    })						
  }
})();

I tried to extract the asynchronous looping and ended up with the following function:

function asyncLoop(collection, seedResult, loopFn, completionFn) {
  var copy = collection.slice(0);
  (function loop() {
    var item = copy.shift();
 
    if(copy.length == 0) {
      completionFn(seedResult);
    } else {
      loopFn(item, seedResult, loop);
    }	
  })();	
}

Which could be called like this:

var people = ["Marc", "Liz", "Ken", "Duncan", "Uday", "Mark", "Charles"];
asyncLoop(people, [], function(name, grid, callBackFn) {
  // parse data and create something cool
  grid.push(somethingCool);
  callBackFn();
}, function(grid) {
  // do something with grid
});

I’m not sure that it reads that much clearer but it does push some of the boiler plate code away.

Written by Mark Needham

September 25th, 2011 at 9:26 am

Posted in Javascript,jQuery

Tagged with ,

jQuery UI Tabs: Changing selected tab

with 12 comments

We’re using the tabs part of the jQuery UI library on the project I’m currently working on and one thing we wanted to do was change the default tab that was being selected.

The documentation suggested that one way to do this was to give the index of the tab we wanted selected when calling the tabs function:

$( ".selector" ).tabs({ selected: 3 });

Since we wanted to select the tab by name based on a value from the query string we thought it would probably be simpler if we could just set the selected tab using a css class.

Our initial thought was that we could put the ‘ui-tabs-hide’ class on the divs that we wanted to hide and then not put that class on the one that we wanted to show.

Unfortunately that didn’t work and the first tab was still being selected…

We downloaded version 1.8.2 of the library via Google’s CDN (which seems really cool!) and were able to see that our class was actually intentionally being removed!

10523
10524
if (o.selected >= 0 && this.anchors.length) { // check for length avoids error when initializing empty list
				this.panels.eq(o.selected).removeClass('ui-tabs-hide');

Luckily a little further down the file there is a comment which explains some other ways to manipulate the selected tab:

10749
10750
10751
10752
10753
			// Selected tab
			// use "selected" option or try to retrieve:
			// 1. from fragment identifier in url
			// 2. from cookie
			// 3. from selected class attribute on <li>

We need to put the class ‘ui-tabs-selected’ on the appropriate <li> and then that will be the one that gets selected.

Written by Mark Needham

September 8th, 2010 at 6:32 pm

Posted in jQuery

Tagged with

jQuery: Dynamically updating a drop down list

with 3 comments

We recently had a requirement to dynamically update a drop down list based on how the user had filled in other parts of the page.

Our initial approach was to populate the drop down with all potential options on page load and then add CSS selectors to the options that we wanted to hide. That worked fine in Chrome and Firefox but Internet Explorer seems to ignore CSS selectors inside a drop down list so none of the options were being hidden.

We therefore had to try and dynamically add and remove options from the drop down list instead.

The list that we initially loaded onto the page was like this:

<select id="foo" name="foo">
<option value="A" selected="selected">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="Not applicable">Not applicable</option>
</select>

Christian eventually came up with the following solution to hide/show those options and select the appropriate one after several hours of trial and error:

// captured on page load
var originalFooOptions = $("#foo > option");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var foo = $("#foo");
var dependentField = $("#fieldFooIsDependentOn");
 
if(notApplicable()) {
	foo.children("option[value!='Not applicable']").remove();
	foo.val("Not applicable");
} else {
	foo.empty();
	$(originalFooOptions).each(function() {
		var newOption = $(this).clone();
		if ($(this).val() === dependentField.val()) {
			newOption.attr("selected", "selected");
		}
		newOption.appendTo(foo);
	});	
}

Another approach we tried was to try and dynamically update ‘foo’ rather than removing all the items and then adding them back.

Unfortunately we kept getting an ‘unspecified error’ on Internet Explorer 6 when we tried to set the selected value with that approach.

Written by Mark Needham

June 30th, 2010 at 10:46 am

Posted in jQuery

Tagged with

jQuery: $.post, ‘jsonp’ and cross-domain requests

with 19 comments

We spent a bit of time yesterday looking through the jQuery code trying to work out why a cross domain request we were making using jQuery’s ‘$.post’ function wasn’t working.

In hindsight perhaps it should have been obvious that you wouldn’t be able to do that but I didn’t completely understand how we were able to do cross domain requests were possible at all but we had some ‘$.getJson’ ‘jsonp’ function calls around our code base which were doing just that.

The jQuery documentation seemed to suggest it was possible to do a ‘jsonp’ ‘POST’ request but when we tried to do so we got the following error:

Error: uncaught exception: [Exception... "Access to restricted URI denied" code: "1012" nsresult: "0x805303f4 (NS_ERROR_DOM_BAD_URI)"

The failure was occurring in this part of the code on line 3517 inside the ‘ajax’ function:

3512
3513
3514
3515
3516
3517
		// Open the socket
		// Passing null username, generates a login popup on Opera (#2865)
		if( s.username )
			xhr.open(type, s.url, s.async, s.username, s.password);
		else
			xhr.open(type, s.url, s.async);

I had initially thought that passing in ‘jsonp’ to the function did something clever to fool the browser into sending the Xml Http Request but actually in a thread from the jQuery mailing list from a year ago where Michael Geary explains what’s actually happening:

Cross-domain JSONP isn’t AJAX at all. It doesn’t use XMLHttpRequest. It’s nothing more than a dynamic script element that loads JavaScript code.

You can’t do a POST with a dynamic script element. Where would you put the POST data?

I don’t know what the $.ajax code is trying to do – maybe it should fail in a more informative way. It will fail one way or another regardless.

We could see where this was being done in the jQuery code:

3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
		// If we're requesting a remote document
		// and trying to load JSON or Script with a GET
		if ( s.dataType == "script" && type == "GET" && parts
			&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
 
			var head = document.getElementsByTagName("head")[0];
			var script = document.createElement("script");
			script.src = s.url;
			if (s.scriptCharset)
				script.charset = s.scriptCharset;
 
			...
 
			head.appendChild(script);
			...
		}

On line 3477 a script tag is dynamically added into the page and on line 3478 we set ‘src’ to be the url for our cross domain request.

We can see on line 3473 that this only happens if we have a ‘GET’ request.

It turned out for us that we were actually only doing this cross domain request on one of our early test environments and that in latter test environments we have everything running on the same domain.

On this occasion we have decided to stop using this environment since it’s not reflective of what our application will run like in production but if we wanted to do a cross domain request then we would need to make use of the ‘$.get’ method with ‘jsonp’ passed as an option.

I was talking about this with Dave Yeung at our coding dojo last night and he pointed me to an article describing how Firefox 3.5 is now supporting the ‘access control for cross site requests’ recommendation which will allow cross domain XHR requests to happen by providing some extra header tags and then reading some additional tags sent back in the response where the server on the other domain can decide which domains are allowed to make calls to it.

I’m still learning this stuff so if anything I’ve said isn’t accurate please point that out.

Written by Mark Needham

August 27th, 2009 at 10:39 pm

Posted in jQuery

Tagged with

jQuery: Approaches to testing

with 4 comments

We’ve been doing a bit of work with jQuery and true to our TDD roots we’ve been trying to work out the best way to test drive our coding in this area.

There seem to be 3 main ways that you can go about doing this, regardless of the testing framework you choose to you. We are using screw-unit for our javascript testing.

Mock everything out

The idea here is that we mock out all calls made to jQuery functions and then we assert that the expected calls were made in our test.

Taking this approach means we are able to reduce the dependencies in our tests and they run very quickly.

The problem is that a lot of assertions become assertions checking that certain operations were called on the DOM since jQuery makes a lot of these type of calls in its code. The tests therefore end up being quite long and difficult to understand unless you were the one who initially wrote them.

We also effectively end up testing how the framework interacts with the DOM rather than testing our own code with this approach.

Don’t mock anything

The opposite approach is to just test directly against jQuery and then do assertions against the part of the DOM affected by the javascript code we are testing.

The problem here is that if you want to test against plugins which make ajax requests or carry out animation effects then the tests become dependent on how long these calls take and we need to find a way to block the test until those calls return which is quite difficult!

Only stub certain calls

The happy medium is that we test directly against the jQuery library but stub out ajax requests and animation effects. Our test assertions are against the state of the DOM to check that it was changed in the way that we expected.

This approach allows us to test our javascript code in terms of its behaviour without testing the internals of how jQuery works.

The wiring up of events to different controls on the page is done in our test but the actual logic that happens when these events are fired is in our js file under test.

We currently favour this last approach as it seems to give us the best of both worlds so to speak. It be would be interesting to hear how are other people going about Javascript testing.

Written by Mark Needham

January 24th, 2009 at 9:36 am

Posted in jQuery

Tagged with

jQuery datepicker IE6 positioning bug

with 9 comments

We’ve been using the jQuery datepicker on my current project and came across some strange behaviour with regards to the positioning of the calendar in IE6.

The calendar was always positioning itself right at the top of the screen instead of just below the textbox it was hooked up to but in Firefox it was working fine.

After a bit of exploration in the jQuery code (ui.datepicker.js) we worked out that the ‘document.documentElement.clientHeight’ call in the ‘_checkOffset’ function was always returning a value of 0 meaning that the position of the calendar was always right at the top of the screen.

_checkOffset: function(inst, offset, isFixed) {
		var pos = inst.input ? this._findPos(inst.input[0]) : null;
		var browserWidth = window.innerWidth || document.documentElement.clientWidth;
		var browserHeight = window.innerHeight || document.documentElement.clientHeight;
...

It turned that we were missing the doctype at the top of the HTML page which make the page standards compliant and as a result document.documentElement.clientHeight returns the proper height.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

Not having this set puts IE into Quirks Mode meaning that document.documentElement.clientHeight always returns 0.

We found a post on the Webmaster World Forum which helps explain why this change makes a difference.

Written by Mark Needham

January 6th, 2009 at 9:57 pm

Posted in jQuery

Tagged with ,

Javascript: Creating quick feedback loops

with one comment

I’ve been working quite a lot with Javascript and in particular jQuery recently and since I haven’t done much in this area before all the tips and tricks are new to me.

One thing which is always useful no matter the programming language is to use it in a way that you can get rapid feedback on what you are doing.

Fortunately there are quite a few tools that allow us to do this with Javascript:

Firebug

The Firefox plugin is perhaps the quickest way of getting feedback on anything Javascript and indeed CSS related.

The ability to see which HTTP calls have been made on a page is invaluable for checking whether AJAX functionality is working correctly and DOM manipulation can be executed and tested on the fly.

Including jQuery in a page effectively makes Firebug the jQuery Interactive Console, allowing us to try out the different functions and see their effects immediately.

Unit Testing Frameworks

There are several javascript unit testing frameworks around at the moment which run in the browser and provide the ability to write assertions on our code.

I have been using QUnit and screw-unit and while they work reasonably well for simple tests, neither seems to be at the level of JUnit or NUnit for example. I’m sure this will come as they mature.

Other frameworks I’ve heard about but not tried: RhinoUnit, JSNUnit, JSUnit, no doubt there are others I haven’t heard about yet.

Selenium IDE

The sometimes forgotten Firefox plugin is very useful for quickly creating repeatable scenarios to see the impact that code changes have had.

The beauty of this approach is that it takes out the manual steps in the process, so we can just make our changes and then re-run the test. The runner lights up green or red, taking out the need to manually verify the correctness of our assertions.

Alert

The ‘alert’ function is perhaps most useful when we want to quickly verify the path being taken through a piece of code without having to step through it using the Firebug debugger.

It’s probably more useful for proving our assumptions than actual debugging and it’s certainly quick and easy to set up.

Written by Mark Needham

December 9th, 2008 at 9:13 pm

jQuery Validation & Firefox Refresh Behaviour

with one comment

We’ve been working quite a bit with jQuery and cross browser compatibility and one of the interesting differences we came across today was the behaviour of Firefox and Internet Explorer when it comes to refreshing a page.

When you press refresh in Internet Explorer the page gets refreshed to the state that it was in when you first loaded the URL, meaning that the state of the data in forms is returned to its original state.

Doing the same in Firefox doesn’t have the same behaviour – the page refreshes but it keeps the most recent data that was entered into the form. In theory this is quite nice behaviour because it means if you accidentally hit refresh it keeps all your data.

For us it was quite annoying as we had hooked up a validator and some other custom code which declared certain parts of the page invalid if any data had changed from what was on the page when it first loaded.

The way we got around the problem was to fire the validation events on jQuery’s document ready as well as firing them on the change events for each of the form elements.

$(document).ready(function(){
   // validation code here as well as on change events for form elements
 });

Although it does mean that we are calling the validation in more places it has helped us to get around the problem.

Written by Mark Needham

December 2nd, 2008 at 10:54 pm

Posted in jQuery

Tagged with , ,