Ajax Bestiary: A Javascript Field Guide
 
Ajax Bestiary: A Javascript Field Guide
 
 

Entries from October 2009

Cross Site Scripting – the new old way

Posted by Dave Mahon

Cross Site Scripting (XSS) is a big security no-no. It’s never supposed to happen, because as we all know, any script operating within your page has full access to the entire DOM of the page.

Then again, there is so much functionality that we want to implement without reinventing the wheel. Marketing departments want to use third-party tracking tools. The IT folks want to distribute the load for our network heavy site across the third-level domains www.domain.com, static.domain.com and data.domain.com. And users want more functionality than we can hope to provide on our own.

So resigned to the reality that XSS is a legitimate necessity, we need a way to do it. The old way (as far back as the mid-90’s, in fact) was straightforward:

document.write(<script language=”JavaScript” type=”text/javascript” src=”http://data.domain.com/myscript.js”></script>’);

This is of course problematic as it treats DOM nodes improperly and may not even get processed by modern browsers. Instead, jQuery provides access to JSONP which will do the same thing – insert a new script node in the DOM – but do it in an XHTML compliant way.

Accessing JSONP is pretty straightforward:

$.getJSON("http://data.domain.com/myscript&callback=?”, inPageFunction);

The key bit is &callback=? – this causes jQuery to insert a script node into the DOM, which is then immediately executed and returned to the callback function, inPageFunction, like so:

inPageFunction({data:"foo";})

If you forget &callback=? you’ll get the error Access to restricted URI denied. And yes, since we’re inserting a script node, we’re limited to GET requests. And if the call fails, you’ll simply get nothing back – no useful error messages.

Further, the next generation of browsers is coming out with integrated support for cross site XMLHttpRequest, but since when have we not had to code to support multiple versions of browsers?

There are loads of good guides to expand your knowledge:

Using the jQuery data method as a local datastore

Posted by Dave Mahon

At some point, we all have to store datasets on the client. We can clutter the namespace with ever more variables and try really hard to avoid collisions. We can cram variables into some local object that we’re using in the normal execution of our script anyway. We can attach custom attributes to HTML nodes, violating the sacrosanct purity of XHTML.

Or we can attach the document-specific variables, in script (thus keeping those automated validators happy), to the document itself. This fairly intuitive approach has not been given the notice it deserves.

Some advantages:

  1. Arbitrary value names can be assigned and we can basically assign as many of these variables as we want.
  2. They can be applied to anything jQuery can access, meaning that we can rapidly assign values to entire sets of DOM nodes without having to add CSS classes or clutter the markup.
  3. They can be dynamically added, accessed, changed and removed at will, without network transfers.

There is the disadvantage that they are not persistent across page loads, but we’ve lived with that problem for a long, long time, haven’t we?

So how do we use this handy method? The documentation is pretty clear cut, but I’ll give a slightly more grounded example:

My HTML is very simplistic in this example:

1
2
3
4
5
6
7
8
<body>
 <div>
  <span id="stats"></span>
  <button id="add">Add Data Point</button>
  <button id="reset">Reset Set</button>
 </div>
 <div id="data"></div>
</body>

The JavaScript is similarly intuitive:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
appExample = {
 addDataPoint: function() {
  var dat = this.getDataSet();
  dat[dat.length] = Math.floor(Math.random()*101);
  $('#data').data('points', dat);
 },
 resetDataSet: function() {
  $('#data').removeData('points');
 },
 dataSize: function() {
  var dat = this.getDataSet();
  return dat.length;
 },
 getDataSet: function() {
  var dat = $('#data').data('points');
  if (undefined == dat) return [];
  if (Array != dat.constructor) return [dat];
  return dat;
 },
 updateDisplay: function() {
  $('#stats').text('Data Points: ' + (lim = this.dataSize()));
  var tmp = '';
  var dat = this.getDataSet();
  for (var i = 0; i < lim; i++) {
   tmp += dat[i];
   if (i < lim - 1) tmp += ',';
  }
  $('#data').text('[' + tmp + ']');
 }
}
 
$(document).ready( function() {
 appExample.updateDisplay();
 $('#add').bind('click', function(e) {
  appExample.addDataPoint();
  appExample.updateDisplay();
 });
 $('#reset').bind('click', function(e) {
  appExample.resetDataSet();
  appExample.updateDisplay();
 });
});

In this case, I’m storing everything as an array in points. While I’m storing randomly generated integers, you can just as easily store entire objects, opening the possibility of a JSON solution.

Note that we can identify those objects lacking the chosen dynamic attribute by calling it and testing for undefined.

The future of Ajax Bestiary

Posted by Don Albrecht

One of my colleagues pointed out the rather high ranking of Ajax Bestiary in his Net News Wire Dinosaur Report today.  While 2nd place isn’t the worst possible ranking, It demonstrates that I haven’t posted anything in quite a while.

I’ve had the best of intentions to resume blogging, but time concerns and the high level of secrecy around my current projects have both pushed me away from regular blog updates.  I’ll be making a bigger effort in the future, but I’ve also recruited a second author for the blog.  David Mahon is a talented front-end web developer who has agreed to produce regular content for AB.

You can expect Dave’s first post sometime in the next few days.