Slideshare.net (beta)

 
Post: 
Myspace Hi5 Friendster Xanga LiveJournal Facebook Blogger Tagged Typepad Freewebs BlackPlanet gigya icons



All comments

Add a comment on Slide 1

If you have a SlideShare account, login to comment; else you can comment as a guest


Showing 1-50 of 56 (more)

Unobtrusive JavaScript with jQuery

From simon, 2 months ago

Slides from a three hour tutorial presented at XTech 2008 on the 6 more

19002 views  |  6 comments  |  53 favorites  |  368 downloads  |  69 embeds (Stats)
 

Tags

xtech08 xtech javascript unobtrusivejavascript jquery unobtrusive js ajax tutorial web

more

 
 
 
 

Privacy InfoNew!

This slideshow is Public

 
Embed in your blog
Embed (wordpress.com)
custom

Slideshow Statistics
Total Views: 19002
on Slideshare: 10400
from embeds: 8602* * Views from embeds since 21 Aug, 07

Slideshow transcript

Slide 1: Unobtrusive JavaScript with jQuery Simon Willison XTech , 6th May 2008

Slide 2: How I learned to stop worrying and love JavaScript

Slide 3: Unobtrusive A set of principles for writing accessible, JavaScript maintainable JavaScript A library that supports unobtrusive scripting

Slide 4: Unobtrusive JavaScript Theory jQuery Practice

Slide 5: We will cover • The what and why of unobtrusive JavaScript • Why use a library at all? • Why pick jQuery? • How jQuery handles... • DOM manipulation • Event handling • Animation • Ajax

Slide 6: Unobtrusive JavaScript

Slide 7: Progressive enhancement Rather than hoping for graceful degradation, PE builds documents for the least capable or differently capable devices first, then moves on to enhance those documents with separate logic for presentation, in ways that don't place an undue burden on baseline devices but which allow a richer experience for those users with modern graphical browser software. Steven Champeon and Nick Finck, 2003

Slide 8: Applied to JavaScript • Build a site that works without JavaScript • Use JavaScript to enhance that site to provide a better user experience: easier to interact with, faster, more fun

Slide 9: • Start with Plain Old Semantic HTML • Layer on some CSS (in an external stylesheet) to apply the site’s visual design • Layer on some JavaScript (in an external script file) to apply the site’s enhanced behaviour

Slide 10: Surely everyone has JavaScript these days?

Slide 11: • There are legitimate reasons to switch it off • Some companies strip JavaScript at the firewall • Some people run the NoScript Firefox extension to protect themselves from common XSS and CSRF vulnerabilities • Many mobile devices ignore JS entirely • Screen readers DO execute JavaScript, but accessibility issues mean that you may not want them to

Slide 12: The NoScript extension

Slide 13: Unobtrusive examples

Slide 14: labels.js • One of the earliest examples of this technique, created by Aaron Boodman (now of Greasemonkey and Google Gears fame)

Slide 17: How it works <label for="search">Search</label> <input type="text" id="search" name="q"> • 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

Slide 18: 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

Slide 21: http://www.neighbourhoodfixit.com/

Slide 24: Implementing Terms and Conditions

Slide 25: Bad Have you read our <a href="javascript:window.open( 'terms.html', 'popup', 'height=500,width=400,toolbar=no' );">terms and conditions</a>?

Slide 26: Also bad Have you read our <a href="#" onclick="window.open( 'terms.html', 'popup', 'height=500,width=400,toolbar=no' ); return false;" >terms and conditions</a>?

Slide 27: Better Have you read our <a href="terms.html" onclick="window.open( 'terms.html', 'popup', 'height=500,width=400,toolbar=no' ); return false;" >terms and conditions</a>?

Slide 28: Better Have you read our <a href="terms.html" onclick="window.open( this.href, 'popup', 'height=500,width=400,toolbar=no' ); return false;" >terms and conditions</a>?

Slide 29: Best Have you read our <a href="terms.html" class="sidenote" >terms and conditions</a>?

Slide 30: Characteristics of unobtrusive scripts • No in-line event handlers • All code is contained in external .js files • The site remains usable without JavaScript • Existing links and forms are repurposed • JavaScript dependent elements are dynamically added to the page

Slide 31: JavaScript for sidenotes • When the page has finished loading... • Find all links with class “sidenote” • When they’re clicked: • Launch a popup window containing the linked page • Don’t navigate to the page

Slide 32: With JavaScript window.onload = function() { var links = document.getElementsByTagName('a'); for (var i = 0, link; link = links[i]; i++) { if (link.className == 'sidenote') { link.onclick = function() { var href = this.href; window.open(this.href, 'popup', 'height=500,width=400,toolbar=no'); return false; } } } }

Slide 33: With JavaScript window.onload = function() { var links = document.getElementsByTagName('a'); for (var i = 0, link; link = links[i]; i++) { if (link.className == 'sidenote') { link.onclick = function() { var href = this.href; window.open(this.href, 'popup', 'height=500,width=400,toolbar=no'); return false; } } } }

Slide 34: With JavaScript window.onload = function() { var links = document.getElementsByTagName('a'); for (var i = 0, link; link = links[i]; i++) { if (link.className == 'sidenote') { link.onclick = function() { var href = this.href; window.open(this.href, 'popup', 'height=500,width=400,toolbar=no'); return false; } } } }

Slide 35: With JavaScript window.onload = function() { var links = document.getElementsByTagName('a'); for (var i = 0, link; link = links[i]; i++) { if (link.className == 'sidenote') { link.onclick = function() { var href = this.href; window.open(href, 'popup', 'height=500,width=400,toolbar=no'); return false; } } } }

Slide 36: Problems

Slide 37: Problems • This only executes when the page has completely loaded, including all images • It over-writes existing load or click handlers • It can’t handle class="sidenote external" • It leaks memory in IE 6

Slide 38: Problems • This only executes when the page has completely loaded, including all images • It over-writes existing load or click handlers • It can’t handle class="sidenote external" • It leaks memory in IE 6 • Solving these problems requires cross- browser workarounds. That’s where libraries come in

Slide 39: With jQuery jQuery(document).ready(function() { $('a.sidenote').click(function() { var href = $(this).attr('href'); window.open(href, 'popup', 'height=500,width=400,toolbar=no'); return false; }); });

Slide 40: With jQuery jQuery(document).ready(function() { jQuery('a.sidenote').click(function() { var href = $(this).attr('href'); window.open(href, 'popup', 'height=500,width=400,toolbar=no'); return false; }); });

Slide 41: With jQuery jQuery(document).ready(function() { jQuery('a.sidenote').click(function() { var href = jQuery(this).attr('href'); window.open(href, 'popup', 'height=500,width=400,toolbar=no'); return false; }); });

Slide 42: With jQuery jQuery(document).ready(function() { jQuery('a.sidenote').click(function() { var href = jQuery(this).attr('href'); window.open(href, 'popup', 'height=500,width=400,toolbar=no'); return false; }); });

Slide 43: With jQuery jQuery(function() { jQuery('a.sidenote').click(function() { var href = jQuery(this).attr('href'); window.open(href, 'popup', 'height=500,width=400,toolbar=no'); return false; }); });

Slide 44: With jQuery $(function() { $('a.sidenote').click(function() { var href = $(this).attr('href'); window.open(href, 'popup', 'height=500,width=400,toolbar=no'); return false; }); });

Slide 45: With jQuery jQuery(function($) { $('a.sidenote').click(function() { var href = $(this).attr('href'); window.open(href, 'popup', 'height=500,width=400,toolbar=no'); return false; }); });

Slide 46: Advantages • jQuery(document).ready() executes as soon as the DOM is ready • $('a.sidenote') uses a CSS selector to traverse the DOM • .click(function() { ... }) deals with cross- browser event handling for us • It also avoids IE memory leaks

Slide 47: Why jQuery instead of $X? • Unlike Prototype and mooTools... • ... it doesn’t clutter your global namespace • Unlike YUI... • it’s succinct • YAHOO.util.Dom.getElementsByClassName() • Unlike Dojo... • ... the learning curve is hours, not days

Slide 48: jQuery characteristics

Slide 49: jQuery characteristics • Minimal namespace impact (one symbol)

Slide 50: jQuery characteristics • Minimal namespace impact (one symbol) • Focus on the interaction between JavaScript and HTML

Slide 51: jQuery characteristics • Minimal namespace impact (one symbol) • Focus on the interaction between JavaScript and HTML • (Almost) every operation boils down to: • Find some elements • Do things with them

Slide 52: jQuery characteristics • Minimal namespace impact (one symbol) • Focus on the interaction between JavaScript and HTML • (Almost) every operation boils down to: • Find some elements • Do things with them • Method chaining for shorter code

Slide 53: jQuery characteristics • Minimal namespace impact (one symbol) • Focus on the interaction between JavaScript and HTML • (Almost) every operation boils down to: • Find some elements • Do things with them • Method chaining for shorter code • Extensible with plugins

Slide 54: Essential tools Firebug extension for Firefox “Inject jQuery” bookmarklet http://icanhaz.com/xtechjs/ jQuery API docs, inc. visualjquery.com

Slide 55: Only one function! • Absolutely everything* starts with a call to the jQuery() function • Since it’s called so often, the $ variable is set up as an alias to jQuery • If you’re also using another library you can revert to the previous $ function with jQuery.noConflict(); * not entirely true

Slide 56: CSS selectors jQuery('#nav') jQuery('div#intro h2') jQuery('#nav li.current a')

Slide 57: CSS selectors $('#nav') $('div#intro h2') $('#nav li.current a')

Slide 58: CSS 2 and 3 selectors a[rel] a[rel="friend"] a[href^="http://"] ul#nav > li li#current ~ li (li siblings that follow #current) li:first-child, li:last-child, li:nth-child(3)

Slide 59: Custom jQuery selectors :first, :last, :even, :odd :header :hidden, :visible :even, :odd :input, :text, :password, :radio, :submit... :checked, :selected, :enabled, :disabled div:has(a), div:contains(Hello), div:not(.entry) :animated

Slide 60: jQuery collections • $('div.section') returns a jQuery collection object • You can call treat it like an array $('div.section').length = no. of matched elements $('div.section')[0] - the first div DOM element $('div.section')[1] $('div.section')[2]

Slide 61: jQuery collections • $('div.section') returns a jQuery collection object • You can call methods on it: $('div.section').size() = no. of matched elements $('div.section').each(function() { console.log(this); });

Slide 62: jQuery collections • $('div.section') returns a jQuery collection object • You can call methods on it: $('div.section').size() = no. of matched elements $('div.section').each(function(i) { console.log("Item " + i + " is ", this); });

Slide 63: jQuery collections • $('div.section') returns a jQuery collection object • You can chain method calls together: $('div.section').addClass('foo').hide(); $('div.section').each(function(i) { console.log("Item " + i + " is ", this); });

Slide 64: The jQuery() function • Overloaded: behaviour depends on the type of the arguments • Grab elements using a selector • “Upgrade” existing DOM nodes • Create a new node from an HTML string • Schedule a function for onDomReady • Usually returns a jQuery collection object

Slide 65: jQuery methods • I’ve identified four key types of jQuery method: • Introspectors - return data about the selected nodes • Modifiers - alter the selected nodes in some way • Navigators - traverse the DOM, change the selection • DOM modifiers - move nodes within the DOM

Slide 66: Introspectors • $('div:first').attr('title') • $('div:first').html() • $('div:first').text() • $('div:first').css('color') • $('div:first').is('.entry')

Slide 67: Modifiers • $('div:first').attr('title', 'The first div') • $('div:first').html('New <em>content</em>') • $('div:first').text('New text content') • $('div:first').css('color', 'red')

Slide 68: Bulk modifiers • $('a:first').attr({ 'title': 'First link on the page', 'href': 'http://2008.xtech.org/' }); • $('a:first').css({ 'color': 'red', 'backgroundColor': 'blue' });

Slide 69: Notice a pattern? • $(selector).attr(name) gets • $(selector).css(name) gets • $(selector).attr(name, value) sets • $(selector).css(name, value) sets • $(selector).attr({ object }) sets in bulk • $(selector).css({ object }) sets in bulk

Slide 70: Style modifiers • $(selector).css(...) • $(selector).addClass(class) • $(selector).removeClass(class) • $(selector).hasClass(class) • $(selector).toggleClass(class)

Slide 71: Dimensions • $(selector).height() • $(selector).height(200) • $(selector).width() • $(selector).width(200) • var offset = $(selector).offset() • offset.top, offset.left

Slide 72: Navigators - finding • $('h1').add('h2') • $('a:first').siblings() • $('div:first').find('a') • $('h3').next() • $('a:first').children() • $('h3:first').nextAll() • $('a:first').children('em') • $('h3').prev() • $('a').parent() • $('h3').prevAll() • $('a:first').parents() • $('a:first').contents()

Slide 73: Navigators - filtering • $('div').eq(1) // gets second • $('div').not('.entry') • $('div').filter('.entry') • $('div').slice(1, 3) // 2nd,3rd • $('div').filter(function(i) { • $('div').slice(-1) // last return this.className == 'foo' }

Slide 74: DOM modifiers • els.append(content) • content.insertAfter(els) • content.appendTo(els) • content.insertBefore(els) • els.prepend(content) • els.wrapAll('<div /'>) • content.prependTo(els) • els.wrapInner('<div /'>) • els.after(content) • els.empty() • els.before(content) • els.remove()

Slide 75: DOM construction • var p = $('<p id="foo" />').addClass('bar'); • p.text('This is some text').css('color', 'red'); • p.appendTo(document.body);

Slide 76: jQuery and Microformats

Slide 77: Favourite restaurant list

Slide 78: With JavaScript enabled

Slide 79: jQuery and Microformats <ul class="restaurants"> <li class="vcard"> <h3><a class="fn org url" href="..."> Riddle &amp; Finns</a></h3> <div class="adr"> <p class="street-address">12b Meeting House Lane</p> <p><span class="locality">Brighton</span>, <abbr class="country-name" title="United Kingdom">UK</abbr></p> <p class="postal-code">BN1 1HB</p> </div> <p>Telephone: <span class="tel">+44 (0)1273 323 008</ span></p> <p class="geo">Lat/Lon: <span class="latitude">50.822563</span>, <span class="longitude">-0.140457</span> </p> </li> ...

Slide 80: Creating the map jQuery(function($) { // First create a div to host the map var themap = $('<div id="themap"></div>').css({ 'width': '90%', 'height': '400px' }).insertBefore('ul.restaurants'); // Now initialise the map var mapstraction = new Mapstraction('themap','google'); mapstraction.addControls({ zoom: 'large', map_type: true });

Slide 81: Creating the map jQuery(function($) { // First create a div to host the map var themap = $('<div id="themap"></div>').css({ 'width': '90%', 'height': '400px' }).insertBefore('ul.restaurants'); // Now initialise the map var mapstraction = new Mapstraction('themap','google'); mapstraction.addControls({ zoom: 'large', map_type: true });

Slide 82: Displaying the map // Show map centred on Brighton mapstraction.setCenterAndZoom( new LatLonPoint(50.8242, -0.14008), 15 // Zoom level );

Slide 83: Extracting the microformats $('.vcard').each(function() { var hcard = $(this); var latitude = hcard.find('.geo .latitude').text(); var longitude = hcard.find('.geo .longitude').text(); var marker = new Marker(new LatLonPoint(latitude, longitude)); marker.setInfoBubble( '<div class="bubble">' + hcard.html() + '</div>' ); mapstraction.addMarker(marker); });

Slide 84: Extracting the microformats $('.vcard').each(function() { var hcard = $(this); var latitude = hcard.find('.geo .latitude').text(); var longitude = hcard.find('.geo .longitude').text(); var marker = new Marker(new LatLonPoint(latitude, longitude)); marker.setInfoBubble( '<div class="bubble">' + hcard.html() + '</div>' ); mapstraction.addMarker(marker); });

Slide 85: Events $('a:first').bind('click', function() { $(this).css('backgroundColor' ,'red'); return false; });

Slide 86: Events $('a:first').click(function() { $(this).css('backgroundColor' ,'red'); return false; });

Slide 87: Event objects $('a:first').click(function(ev) { $(this).css('backgroundColor' ,'red'); ev.preventDefault(); });

Slide 88: Triggering events $('a:first').trigger('click');

Slide 89: Triggering events $('a:first').click();

Slide 90: Events • blur() • keydown() • mouseup() • change() • keypress() • resize() • click() • keyup() • scroll() • dblclick() • load() • select() • error() • mousedown() • submit() • focus() • mouseover() • unload()

Slide 91: labels.js with jQuery ... <label class="inputHint" for="email">E-mail:</label> <input id="email" type="text"> ...

Slide 92: labels.js with jQuery jQuery(function($) { $('label.inputHint').each(function() { var label = $(this); var input = $('#' + label.attr('for')); var initial = label.hide().text().replace(':', ''); input.focus(function() { input.css('color', '#000'); if (input.val() == initial) { input.val(''); } }).blur(function() { if (input.val() == '') { input.val(initial).css('color', '#aaa'); } }).css('color', '#aaa').val(initial); }); });

Slide 93: labels.js with jQuery jQuery(function($) { $('label.inputHint').each(function() { var label = $(this); var input = $('#' + label.attr('for')); var initial = label.hide().text().replace(':', ''); input.focus(function() { input.css('color', '#000'); if (input.val() == initial) { input.val(''); } }).blur(function() { if (input.val() == '') { input.val(initial).css('color', '#aaa'); } }).css('color', '#aaa').val(initial); }); });

Slide 94: labels.js with jQuery jQuery(function($) { $('label.inputHint').each(function() { var label = $(this); var input = $('#' + label.attr('for')); var initial = label.hide().text().replace(':', ''); input.focus(function() { input.css('color', '#000'); if (input.val() == initial) { input.val(''); } }).blur(function() { if (input.val() == '') { input.val(initial).css('color', '#aaa'); } }).css('color', '#aaa').val(initial); }); });

Slide 95: labels.js with jQuery jQuery(function($) { $('label.inputHint').each(function() { var label = $(this); var input = $('#' + label.attr('for')); var initial = label.hide().text().replace(':', ''); input.focus(function() { input.css('color', '#000'); if (input.val() == initial) { input.val(''); } }).blur(function() { if (input.val() == '') { input.val(initial).css('color', '#aaa'); } }).css('color', '#aaa').val(initial); }); });

Slide 96: labels.js with jQuery jQuery(function($) { $('label.inputHint').each(function() { var label = $(this); var input = $('#' + label.attr('for')); var initial = label.hide().text().replace(':', ''); input.focus(function() { input.css('color', '#000'); if (input.val() == initial) { input.val(''); } }).blur(function() { if (input.val() == '') { input.val(initial).css('color', '#aaa'); } }).css('color', '#aaa').val(initial); }); });

Slide 97: Advanced chaining • Modified chains can be reverted using end() $('div.entry').css('border', '1px solid black). find('a').css('color', 'red').end(). find('p').addClass('p-inside-entry'