Thursday, November 15, 2007

Using unobtrusive JavaScripts to extend an ASP.NET product

I previously blogged about using JavaScript to extend the functionality of a closed source ASP.NET product. Now, using similar client-side methods, we're also rolling out bugfixes. This post is about how we used unobtrusive JavaScripts to make the process easier.

The story so far: after falling out with our partner, we've stopped receiving support for a module we sold as part of our solution. We've got access to the ASPX files but not the C# code-behind or business classes. So, we went with JavaScript hacking to dynamically generate iframe containers for our new file upload functionality. Sometime last week, we successfully deployed the customization. The customer has since logged several presentation layer bugs that we now needed to fix e.g. figures not adding up correctly etc.

Although we're able to change markup within the ASPX files, we were careful to introduce changes unobtrusively (in the conventional sense, as applied to JavaScript code, and also by principal in regards to the existing module, so we won't in any way risk breaking an application that's running live). I found rules like the seven in Chris Heilmann's recent post (read also the article) provided for very good best practice guidelines towards our goal.

Area of Impact
Firstly, we minimized change to the existing code base by consolidating all changes into a single JavaScript file, customization.js, which is then loaded dynamically by every webpage in the application.

Assumptions
We started development with assumptions based on existing project requirements i.e. Internet Explorer is the only targeted implementation browser, and JavaScript is required etc. So, we didn't need to worry about non-standard input devices and browsers other than a JScript-enabled IE. This really helped.

Hooks and Relationships
One of the bugfixes required me to retrieve numbers from Label controls in each row of a databound GridView, total them, and display the result in a TextBox control. Upon inspection of the ASP.NET generated HTML, I noticed that the resulting Label control ClientIDs looked like grdMain__ctl3_lblAmount, grdMain__ctl4_lblAmount etc. The TextBox was simpler to access, straight up by its ID. I wrote the DoSum() method to hook onto the HTML elements by searching for spans ids that matched the pattern.


var Customization = {
...
DoSum: function() {
var total = 0;
if(document.getElementById &&
document.getElementsByTagName) {

var labels =
document.getElementsByTagName("span");

var totalTextField =
document.getElementById("txtTotal");

if(labels && totalArea) {
for(var i in labels) {

// Check if the span has an id
// Check if the span's id matches
// Check if the span's text is a number
if(labels[i].id &&
labels[i].id.indexOf("lblAmount")>0 &&
!isNaN(labels[i].innerText)) {

total += parseFloat(labels[i].innerText);
}
}

// write to the text field
totalTextField.value = total;
}
}
}
...
}
...
(function(){
...
Customization.DoSum();
})();


One other enhancement required us to change a form TextField into a DropDownList. I used methods similar to the above to find the TextFields and then, appended a select element to the innerHTML property of the TextField's parent node, and changed the display style property of the TextField to "none". The drop-down list's onchange event handler then updates the invisible TextField accordingly. The possibilities for what you can do are endless, and with unobtrusive JavaScripts patterns, they're a lot more manageable.

Future Development and Maintenance
Concerning further development, we made sure our code is readable, unambiguous and complete with concise comments so we could work with it easily in the future. We encapsulated our code into objects, namespaces and anonymous functions to avoid naming conflicts with both existing and future code.

This also hid the details of our implementation code from the outside world, though in reality, being the sole vendors in this case we didn't have to worry about third party developers.

More reading on unobtrusive JavaScript:
Behavioural Separation, and
The Behavioural Layer: Using JavaScript for Good, not Evil (both by Jeremy Keith)

0 comments: