How to make Ajax work for you

52,606 views
48,810 views

Published on

A three hour Ajax tutorial presented at Web 2.0 Expo Berlin on November 5th 2007.

Published in: Technology
3 Comments
71 Likes
Statistics
Notes
  • Ajax Cleaner Bonus Size, 28 Oz --- http://amzn.to/25cLPZV
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Ajax Cleaner (Pack of 2) --- http://amzn.to/1MipCn6
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Ajax Powder Cleanser with Bleach, 14 oz (396 g) --- http://amzn.to/1SaSe0L
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
52,606
On SlideShare
0
From Embeds
0
Number of Embeds
1,891
Actions
Shares
0
Downloads
2,952
Comments
3
Likes
71
Embeds 0
No embeds

No notes for slide

How to make Ajax work for you

  1. How to make work for you Simon Willison - http://simonwillison.net/ Web 2.0 Expo Berlin 5th November 2007
  2. In this talk • Where Ajax came from • When you should (and shouldn’t) use it • Ajax fundamentals (including a DOM and JavaScript refresher) • Advanced Ajax techniques • Picking and using a JavaScript library
  3. Please ask questions at any time
  4. February 2005
  5. AJAX v.s. Ajax “Asynchronous JavaScript + XML”
  6. AJAX v.s. Ajax “Any technique that “Asynchronous allows the client to JavaScript + XML” retrieve more data from the server without reloading the whole page”
  7. Why did Ajax happen? • Two reasons: • The browsers that mattered all added support for IE’s XMLHttpRequest object (which IE has had since 1998) • Jesse James Garrett came up with a name that sucked a lot less than “XMLHttpRequest” did
  8. Pre-Ajax... • “Remote Scripting” (not as catchy) • Various unpleasant but ingenious hacks: • Scriptable Java applets • Passing data through changes to a cookie • Passing data through a hidden iframe • (That one’s actually still in use)
  9. Ajax scenarios
  10. Some anti-patterns • Drag and drop shopping carts • Massive page updates e.g. pagination • Unlinkable content (see also bookmarking) • Breaking browser expectations • Forgetting the loading icon!
  11. Fundamentals
  12. The three legged stool (X)HTML CSS JavaScript
  13. The three legged stool The DOM (X)HTML CSS JavaScript
  14. The DOM
  15. The DOM
  16. The DOM <html> <head> <body> <title> <div id=quot;headquot;> <div id=quot;mainquot;> <h1> <p> <p> <p>
  17. Key DOM methods • document.getElementsByTagName, document.getElementById • el.childNodes, el.parentNode, el.firstChild, el.nextSibling, el.getAttribute • el.appendChild, el.insertBefore, el.setAttribute, el.removeChild • document.createElement, document.createTextNode http://www.howtocreate.co.uk/tutorials/javascript/domstructure
  18. JavaScript doesn’t suck! • Just remember; your; semicolons; • Avoid accidental globals: always declare your variables with “var” • Take the time to figure out higher order programming, where you treat functions as objects
  19. CSS class switching • Often if you are dynamically making large- scale changes to the layout of a page... • ... you can do most of the work by defining an alternative CSS class ... • ... and then switching a container element’s className property in your JavaScript
  20. Firebug!
  21. Firebug • Inspect HTML elements to see what CSS rules currently apply to them • Interactive console for experimenting with JavaScript against the loaded page • Full JavaScript debugger, including breakpoints • Profile your code and your site’s assets and Ajax requests • Logging with console.log() and friends
  22. XMLHttpRequest • The object that lets you make HTTP requests from within JavaScript • IE had it first, using an ActiveX Object • Almost nothing to do with XML • Asynchronous (so you use callbacks)
  23. XMLHttpRequest • The object that lets you make HTTP requests from within JavaScript • IE had it first, using an ActiveX Object • Almost nothing to do with XML • Asynchronous (so you use callbacks)
  24. var xhr = createXHR(); xhr.onreadystatechange = onComplete; xhr.open('GET', '/helloworld.txt', true); xhr.send(null); function onComplete() { if (xhr.readyState == 4) { if (xhr.status == 200) { alert(xhr.responseText); } else { alert('Error code ' + xhr.status); } } }
  25. var xhr = createXHR(); xhr.onreadystatechange = onComplete; xhr.open('GET', '/helloworld.txt', true); xhr.send(null); function onComplete() { if (xhr.readyState == 4) { if (xhr.status == 200) { alert(xhr.responseText); } else { alert('Error code ' + xhr.status); } } }
  26. var xhr = createXHR(); xhr.onreadystatechange = onComplete; xhr.open('GET', '/helloworld.txt', true); xhr.send(null); function onComplete() { if (xhr.readyState == 4) { if (xhr.status == 200) { alert(xhr.responseText); } else { alert('Error code ' + xhr.status); } } }
  27. var xhr = createXHR(); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200) { alert(xhr.responseText); } else { alert('Error code ' + xhr.status); } } }; xhr.open('GET', '/helloworld.txt', true); xhr.send(null);
  28. function createXHR() { var xhr = null; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { try { xhr = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} } return xhr; }
  29. function createXHR() { var xhr = null; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { try { xhr = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} } return xhr; }
  30. var xhr = createXHR(); xhr.onreadystatechange = onComplete; xhr.open('POST', '/helloworld.php', true); xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' ); xhr.send('name=Simon&age=26');
  31. xhr.open('POST', '/helloworld.php', true); The third argument specifies that we want to make an asynchronous request (“tell me when you’re done by calling this function”). If you set this to false you’ll get a synchronous request, which will completely hang the browser until the request returns. Don’t do that.
  32. Too much boilerplate? • That’s quite a lot to remember • Best option: don’t remember any of it! • Use a good library instead • Lots more on those later on
  33. doRequest doRequest(quot;GETquot;, quot;/blah.txtquot;, null, function(xhr) { doSomethingWith(xhr.responseText); }); doRequest(quot;GETquot;, quot;/getinfo.phpquot;, { quot;namequot;: quot;Simonquot; }, function(xhr) { doSomethingWith(xhr.responseText); }); doRequest(quot;POSTquot;, quot;/getinfo.phpquot;, { quot;namequot;: quot;Simonquot; }, function(xhr) { doSomethingWith(xhr.responseText); });
  34. function doRequest(method, url, args, callback) { var xhr = createXHR(); method = method.toUpperCase(); args = args && buildQueryString(args); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { callback(xhr); } }; if (method == 'GET' && args) { url += '?' + args; args = null; } xhr.open(method, url, true); if (method == 'POST') { xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' ); } xhr.send(args); }
  35. function buildQueryString(obj) { var bits = []; for (var key in obj) { if (obj.hasOwnProperty(key)) { bits[bits.length] = escape(key) + '=' + escape(obj[key]); } } return bits.join('&'); } var s = buildQueryString({ name: 'Simon', age: 26 }); name=Simon&age=26
  36. Data transport options
  37. Client data submission • Requests can have anything you like in the body: • HTML, XML, JSON... • So if you really want to, you can access SOAP or XML-RPC services from JavaScript • For almost all purposes, regular form arguments suffice • REST APIs are a good fit as well
  38. Server responses • Plain text • HTML fragments • CSV or similar • XML • Executable JavaScript • JSON
  39. Plain text • Just sending back a simple value • xhr.responseText is all you need • Pros: • Absurdly simple • Cons: • Only supports a single value or string
  40. HTML fragments • Return an HTML fragment, and inject it using innerHTML function onSuccess(xhr) { var div = document.getElementById('response'); div.innerHTML = xhr.responseText; } <div id=quot;responsequot;> </div>
  41. Fragment pros and cons • Pros: • Again, really simple • You can leverage existing server-side templates and techniques • Cons: • You have to change your server-side code to change client-side behaviour • You can't reuse your endpoint
  42. Executable JavaScript • We can pass JavaScript code back from the server and execute it directly document.getElementById( 'response').innerHTML = 'Done!'; function onSuccess(o) { eval(o.responseText); }
  43. • Pros: • Incredibly simple client-side code • The server can do abolutely anything, without needing to modify the client-side code • Cons: • Weird factoring of code - dynamically generating code to run on the client makes it hard to keep track of the big picture • How about just sending structured data?
  44. CSV or similar • We want structured data! Simon Willison,26,simon@simonwillison.net • Pros: • Easy to parse: xhr.responseText.split(','); • Cons: • How do we pass values containing commas? • Index based, so brittle and hard to maintain
  45. XML • Surely XML will save us! <person> <name>Simon</name> <age>26</age> </person> function onSuccess(xhr) { var dom = xhr.responseXML; showPerson( dom.getElementsByTagName('name')[0].data, parseInt(dom.getElementsByTagName('age')[0].data, 10) }); }
  46. XML pros and cons • Pros: • We can re-use our DOM knowledge • xhr.responseXML parses it for us • We can call existing Web Services directly • Cons: • Verbose handling code • Is this the simplest thing that could possibly work?
  47. JSON • A standard for representing common data structures, quot;discoveredquot; by Douglas Crockford • Common data structures? • Associative arrays (dictionaries / hashes) • Arrays (lists) • Numbers, strings, booleans • A subset of JavaScript syntax
  48. JSON example {quot;namequot;: quot;Simonquot;, quot;agequot;: 26} function onSuccess(xhr) { var person = eval('(' + xhr.responseText + ')') ... )
  49. More complex JSON {quot;peoplequot;: [{ quot;namequot;: quot;Simonquot;, quot;emailquot;: quot;simon@simonwillison.netquot; }, { quot;namequot;: quot;Nataliequot;, quot;emailquot;: quot;nat@natbat.netquot; } ]} function onSuccess(xhr) { var people = eval('(' + xhr.responseText + ')'); ... }
  50. To compare... <people> <person> <name>Simon</name> <email>simon@simonwillison.net</email> </person> <person> <name>Natalie</name> <email>nat@natbat.net</email> </person> </people>
  51. To compare... function onSuccess(o) { var dom = o.responseXML; var people = []; var people_els = dom.getElementsByTagName('people'); for (var i = 0, el; el = people_els[i]; i++) { people[people.length] = { 'name': el.getElementsByTagName('name')[0].data, 'email': el.getElementsByTagName('email')[0].data }; } ... }
  52. JSON v.s. XML JSON XML Bytes (excluding whitespace) 95 156 Lines of code to process 1 7+
  53. It ain’t just for JavaScript • Libraries for consuming and generating JSON are available for many languages, including: • Python • Ruby • PHP • Java ... • JSON works really well as a general purpose tool for exchanging data structures
  54. Aside: XSLT • Modern browsers support XSLT transforms - albeit with differing APIs • These can be significantly faster than regular DOM manipulation • If you're having performance problems, this may be worth looking in to
  55. Recommendations • For quick and simple apps, HTML fragments offer the path of least resistance • For most purposes, JSON makes the most sense • Use XML if you already have an XML Web Service that you want to consume (or you need an XSLT speed boost)
  56. Let’s see some running code
  57. Advanced Ajax topics
  58. Cross-domain Ajax
  59. The same-origin restriction • XMLHttpRequest is only allowed to communicate with the domain from which the page was loaded • The same is true of JavaScript calls between windows, frames and iframes • This is a critical browser security feature
  60. The intranet attack • http://wiki.private.corp/ contains private data • User is tricked in to visiting evilexample.com • evilexample.com uses XMLHttpRequest (or a hidden iframe child) to load and steal data from http://wiki.private.corp/
  61. • JSON-P offers a (hackish) solution to the cross-domain XMLHttpRequest restriction http://example.com/query?name=Simon callback({quot;namequot;: quot;Simonquot;, quot;agequot;: 26}); function loadJSON(url) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url; document.getElementsByTagName('head')[0].appendChild(script); } function callback(person) { ... } loadJSON('http://example.com/query?name=' + name);
  62. • JSON-P offers a (hackish) solution to the cross-domain XMLHttpRequest restriction http://example.com/query?name=Simon callback({quot;namequot;: quot;Simonquot;, quot;agequot;: 26}); function loadJSON(url) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url; document.getElementsByTagName('head')[0].appendChild(script); } function callback(person) { ... } loadJSON('http://example.com/query?name=' + name);
  63. Pros and cons • Pros: • Lets you call services hosted on other domains, provided they opt-in • del.icio.us, Flickr, other sites offer this • Cons: • Detecting errors is harder than with XMLHttpRequest • You have to trust the site who’s code you are executing
  64. XSS
  65. • XSS attacks occur when a malicious third party injects untrusted code (HTML, CSS or JavaScript) in to your page • It’s generally a server-side consideration, but you need to take it in to consideration when writing JavaScript as well • Ajax can compound the problem by allowing users to create self-propagating “worms”
  66. CSRF
  67. CSRF for GET <img src=quot;http://example.com/admin/delete.php?id=5quot;> Trick someone who is signed in to example.com/admin/ in to visiting a page hosting that image and you’ll force them to delete something.
  68. CSRF for POST <form id=quot;evilquot; action=quot;http://example.com/admin/delete.phpquot; method=quot;POSTquot;> <input type=quot;hiddenquot; name=quot;idquot; value=quot;5quot;> </form> <script> document.getElementById('evil').submit() </script>
  69. CSRF protection • For regular forms, add a hidden field with a one-time secret token and check for that token on every submission • For Ajax, either do the above or use: xhr.setRequestHeader( quot;X-Requested-Withquot;, quot;XMLHttpRequestquot; );
  70. Unobtrusive JavaScript
  71. Unobtrusive JavaScript • JavaScript isn't always available • Security conscious organisations (and users) sometimes disable it • Some devices may not support it (mobile phones for example) • Assistive technologies (screen readers) may not play well with it • Search engine crawlers won't execute it • Unobtrusive: stuff still works without it!
  72. Progressive enhancement • Start with solid markup • Use CSS to make it look good • Use JavaScript to enhance the usability of the page • The content remains accessible no matter what
  73. Adding events • Unobtrusive JavaScript relies on adding event handlers after the fact • The naive way of doing this: window.onload = function() { for (var i = 0, link; link = document.links[i]; i++) { if (link.className == 'external') { link.onclick = function() { window.open(this.href); return false; } } } };
  74. Problem • If you later assign something else to window.onload (or one of the link.onclicks) you will clobber your behaviour • You should add events quot;properlyquot;... • Different in different browsers • attachEvent v.s. addEventListener • My advice: use a library!
  75. Unobtrusive examples
  76. labels.js • One of the earliest examples of this technique, created by Aaron Boodman (now of Greasemonkey and Google Gears fame)
  77. How it works <label for=quot;searchquot;>Search</label> <input type=quot;textquot; id=quot;searchquot; name=quot;qquot;> • Once the page has loaded, the JavaScript: • Finds any label elements linked to a text field • Moves their text in to the associated text field • Removes them from the DOM • Sets up the event handlers to remove the descriptive text when the field is focused • Clean, simple, reusable
  78. easytoggle.js • An unobtrusive technique for revealing panels when links are clicked <ul> <li><a href=quot;#panel1quot; class=quot;togglequot;>Panel 1</a></li> <li><a href=quot;#panel2quot; class=quot;togglequot;>Panel 2</a></li> <li><a href=quot;#panel3quot; class=quot;togglequot;>Panel 3</a></li> </ul> <div id=quot;panel1quot;>...</div> <div id=quot;panel2quot;>...</div> <div id=quot;panel3quot;>...</div>
  79. How it works • When the page has loaded... • Find all links with class=quot;togglequot; that reference an internal anchor • Collect the elements that are referenced by those anchors • Hide all but the first • Set up event handlers to reveal different panels when a link is clicked • Without JavaScript, links still jump to the right point
  80. Django filter lists • Large multi-select boxes aren't much fun • Painful to scroll through • Easy to lose track of what you have selected • Django's admin interface uses unobtrusive JavaScript to improve the usability here
  81. • Ajax is often used to avoid page refreshes • So... • Write an app that uses full page refreshes • Use unobtrusive JS to quot;hijackquot; links and form buttons and use Ajax instead • Jeremy Keith coined the term quot;Hijaxquot; to describe this
  82. More progressive Ajax suggestions • Live search / filtering • Adding comments / tags • Smart form validation • Checking if usernames are already taken • You could even make a progressively enhanced chat room
  83. The onload problem • All of these examples use code that runs when the window quot;loadquot; event is fired • Wait until page is loaded, then manipulate the DOM • Problem: If the page takes a while to load (large inline images) there will be a Flash Of Unstyled Content (FOUC) • Also, if the user clicks things before the setup code has fired they won't get the expected behaviour.
  84. document.write(css) • If the effect requires hiding some otherwise visible elements, you can document.write a stylesheet <script type=quot;text/javascriptquot;> document.write('<style type=quot;text/cssquot;>'; document.write('.hideme { display: none }'); document.write('</style>'); </script>
  85. body.onclick • If your effect is triggered by the user clicking on something, attach code to the document.body quot;clickquot; event right at the start of your code (no need to wait for the document quot;loadquot; event) YAHOO.util.Event.on(document.body, 'click' function(e) { var clicked_el = YAHOO.util.Event.getTarget(e); // ... }
  86. onDOMReady • A number of attempts have been made to create an onDOMReady event that fires when the DOM has been constructed but before the page has loaded • dean.edwards.name/weblog/2006/06/again/ • Modern libraries all support this in some form • Problem: CSS may not have loaded, so calculated dimensions may be incorrect
  87. • Unobtrusive JavaScript is the Right Way to go about things, but runs in to browser differences even faster than regular JavaScript • Which leads us neatly on to...
  88. Frameworks and Libraries
  89. JavaScript libraries • ajaxpatterns.org lists over 40 general purpose JavaScript libraries • ... and that’s not including the many libraries tied to a specific server-side language • Why are there so many of them?
  90. “The bad news: JavaScript is broken. The good news: It can be fixed with more JavaScript!” Geek folk saying
  91. • Inconsistent event model (thanks, IE) • Positioning and co-ordinates • Memory management (thanks, IE) • The DOM is a horrible API! • JavaScript-the-language has quite a few warts • But it’s powerful enough to let you fix them • Classes and inheritance can be confusing • Many useful JS utility functions are missing • Drag and drop and Animation are really hard
  92. Narrowing them down... • Prototype (and Scriptaculous) • The Yahoo! User Interface Library - YUI • jQuery • The Dojo Toolkit
  93. Honourable mentions • MochiKit • No updates since early 2006 • The Google Web Toolkit • I don’t do Java • mooTools • Lots of buzz, but I haven’t figured out why yet
  94. Download Get the latest version—1.5.1 Learn Prototype is a JavaScript Framework that aims to Online documentation and resources. ease development of dynamic web applications. Discuss Featuring a unique, easy-to-use toolkit for class-driven development and the nicest Ajax library around, Prototype Mailing list and IRC is quickly becoming the codebase of choice for web application developers everywhere. Contribute Submit patches and report bugs. Prototype and Scriptaculous Prototype and script.aculo.us: The quot;Bungee bookquot; has landed! Who's using Prototype? Meet the developers Core team member Christophe Porteneuve has been hard at work for the past few months tracking
  95. • Prototype focuses on basic browser compatibility and JavaScript language enhancement • Tries to make JavaScript more like Ruby • Extends most of JavaScript’s built-in objects with new functionality • Scriptaculous adds fancy effects, basic widgets and drag and drop
  96. $$('#bmarks li').each(function(li){ Event.observe(li, 'click', function(e) { this.style.backgroundColor = 'yellow'; }.bindAsEventListener(li)); });
  97. The Yahoo UI Library
  98. • Created at Yahoo!, BSD licensed • Designed for both creating new applications and integration with legacy code • Focused on browser issues; almost no functionality relating to JS language itself • Extensively tested and documented
  99. controls calendar container autocomplete menu slider treeview dragdrop animation dom event connection utilities
  100. YAHOO.util.Event.on(window, 'load', function() {

×