Recently in AJAX Category

The Wrong Way to Ajax

| | Comments (1) | TrackBacks (0)

Recently I logged onto LinkedIn, to belatedly answer to connection requests.

LinkedIn now has some fancy ajax stuff, so you can just click accept on an invitation, and instead of loading a page, it does a background request and, if all goes well, that invite just disappears with a success message, leaving you to deal with the next request without leaving the page.

That's great. I did a lot of that when doing web application work for DAZ3D. One of the hallmarks of efficient e-commerce is giving people tools to get things done faster - and that meant things like list filters, type-ahead searches, ajax'd add-to-cart buttons - you name it.

But LinkedIn has an issue. See the screenshot:

I've used Firebug to peek at what is happening when it makes the request. As you can see, there's an error. The first time, I wrote it off as something transient, but I came back days later and got the same error.

I decided to log off and log back on, and the issue was fixed. Clearly, the "remember me" was sufficient for a normal page view, but not sufficient for accepting a connection request.

The problem arises because the ajax request is getting a normal full pageview back with the 500 error. I've written before about detecting ajax; if you're going to send a request over ajax, then whatever is handling the request should be aware, even if it's an error. There's nothing wrong with a 500 code for an ajax request that errors out; but your javascript should be ready to handle it.

In this case, as you can see from the (slightly privacy-screened) screenshot, I had a "spinny" (that's the technical term) that was going to sit there forever. That's especially bad, as a normal user will have no idea if the request has failed or is taking a really long time. Ideally, you throw a good error to the user; or, in this case, since you're not accepting their stale credentials, even redirect them to a login box. What you don't do is just throw a 500 error the javascript can't interpret and then stop there.

I personally like returning JSON objects, and I prefer to return them regardless of the code: 200 or 500, you get JSON if you made an ajax request. Then, my javascript always "fails over" - it looks for success. If there's no success message, it's an error.

Example:

$.get(href, function (data, textStatus) { var res = handleJSON(data); if (res['success']||false) { tgt.after(""+(res['success']||'Subscription added.')+""); $("#subsvcmsg").attr("id", "").fadeOut(2500, function() { $(this).remove(); }); } else { tgt.after(""+(res['error']||'An unknown error occurred trying to modify that subscription')+""); $("#subsvcmsg").attr("id", "").fadeOut(2500, function() { $(this).remove(); }); } } );

As you can see here, this code tests for a success message - using javascript's || operator to avoid any error messages being thrown to the user even if that element doesn't exist - and unless a success message is available, it prints an error. It tries to use the error returned by the server, if one exists - but again, if the error isn't there, it has a default message.

The point here is: don't leave your users hanging. (Also, you may not want to tell people your crack team is springing into action to handle a 500 error if that's not the case and it will still be failing days later, but that's aside from the point.)

jQuery 1.2.6

| | Comments (0) | TrackBacks (0)

A couple weeks ago, the jQuery team released jquery 1.2.6.

jQuery is the write-once, run-anywhere a new type of javascript library.

It's incredibly full of win. Favorite things in 1.2.6:

  • Still small (minified+gzipped, 15k)
  • Dimensions plugin - I can see why. Dimensions was a godsend, providing totally important calls like $.position() and $.innerHeight() and whatnot. Indispensible.
  • makeArray - jQuery's internal .makeArray() method now converts any array-like object into a new array. Additionally it wraps all other objects as an array and returns the resulting set. This one is so wild, it will require further digestion, but there are plenty of possibilities

I'm working on a brief introductions to jQuery I will post to go over a bunch of basic techniques; things that go a tiny bit beyond the jquery.com documentation usage.

When you send a request via $.ajax, $.get, or $.post in jquery, you can test for the HTTP_X_REQUESTED_WITH header, looking for a value of 'XMLHttpRequest', a la:

function isajax() { return ((!empty($_SERVER['HTTP_X_REQUESTED_WITH'])) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'); }

But what happens when you're using the jQuery Form plugin? Normally, things work fine. Below the cut for a serious caveat!

Today I had the opportunity to play with event bubbling and applying generic event handlers. Quick overview on handling them (with jquery!) and which events cannot be cancelled.

dojo.lang.hitch

| | Comments (0) | TrackBacks (0)

A few weeks ago, I was wrestling with one of those things that I used to consider a major pain with javascript: meshing objects and event handling.

Let's say you have some code. Sloppy, but demonstrative:

<html>
<head>
<script lanugage='Javascript' src='dojo/dojo.js'></script>
<script>
dojo.require("dojo.lang.*");
</script>
</head>
<body>
<div id='output' name='output'></div>
<script language='Javascript'>
function foo() {
var x;
var y;
this.go = function() {document.getElementById('output').innerHTML += 'this.x is ' + this.x + '<br />';}
}
var fooz = new foo;
fooz.x = 5;
document.getElementById('output').innerHTML += 'fooz.x is ' + fooz.x + '<br />';
setTimeout(fooz.go, 300);
</script>
</body>
</html>

What happens when you run it?

fooz.x is 5 this.x is undefined

Javascript objects do not stay instantiated when passed into setTimeout. setTimeout() is unavoidable, but I often find that when implementing behaviors which need objects, you're forced into one of two nasty choices:

  • Give everything an id, and pass that id into setTimeout in order to maintain some sort of scope
  • Watch everything lose context when passing through setTimeout

Not pretty choices.

But dojo.hang.hitch() solves that problem, gloriously. dojo.lang.hitch lets you attach a function to run IN THE SCOPE of an object.

Let's change the code above:

<html>
<head>
<script lanugage='Javascript' src='dojo/dojo.js'></script>
<script>
dojo.require("dojo.lang.*");
</script>
</head>
<body>
<div id='output' name='output'></div>
<script language='Javascript'>
function foo() {
var x;
var y;
this.go = function() {document.getElementById('output').innerHTML += 'this.x is ' + this.x + '<br />';}
}
var fooz = new foo;
fooz.x = 5;
document.getElementById('output').innerHTML += 'fooz.x is ' + fooz.x + '<br />';
setTimeout(dojo.lang.hitch(fooz, fooz.go), 300);
</script>
</body>
</html>

Output now:

fooz.x is 5
this.x is 5

Beautiful.

What's great is that this lets you run events on things without IDs. I dynamically instantiate a lot of elements in the DOM with document.createElement, and there's often no NEED to assign an id except for issues like this; handling events. Now, with DOJO's hitching, you can deal with your objects while preserving context through setTimeout calls.

Dojo is filled with gems.

Check out the Dojo Homepage and this little into to dojo isn't bad either.

May 2012

Sun Mon Tue Wed Thu Fri Sat
    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    
Creative Commons License
This weblog is licensed under a Creative Commons License.