unobtrusive javascript with jquery

134
Unobtrusive JavaScript with jQuery Simon Willison XTech , 6th May 2008

Upload: simon-willison

Post on 12-May-2015

88.804 views

Category:

Technology


0 download

DESCRIPTION

Slides from a three hour tutorial presented at XTech 2008 on the 6th of May. See also the supporting notes.

TRANSCRIPT

Page 1: Unobtrusive JavaScript with jQuery

Unobtrusive JavaScript with jQuery

Simon WillisonXTech , 6th May 2008

Page 2: Unobtrusive JavaScript with jQuery

How I learned to stop worrying and love JavaScript

Page 3: Unobtrusive JavaScript with jQuery

Unobtrusive JavaScript

A set of principles for writing accessible, maintainable JavaScript

A library that supports unobtrusive scripting

Page 4: Unobtrusive JavaScript with jQuery

Unobtrusive JavaScript Theory

jQuery Practice

Page 5: Unobtrusive JavaScript with jQuery

• 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

We will cover

Page 6: Unobtrusive JavaScript with jQuery

Unobtrusive JavaScript

Page 7: Unobtrusive JavaScript with jQuery

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.

Progressive enhancement

Steven Champeon and Nick Finck, 2003

Page 8: Unobtrusive JavaScript with jQuery

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

Page 9: Unobtrusive JavaScript with jQuery

• 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

Page 10: Unobtrusive JavaScript with jQuery

Surely everyone has JavaScript these days?

Page 11: Unobtrusive JavaScript with jQuery

• 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

Page 12: Unobtrusive JavaScript with jQuery

The NoScript extension

Page 13: Unobtrusive JavaScript with jQuery

Unobtrusive examples

Page 14: Unobtrusive JavaScript with jQuery

• One of the earliest examples of this technique, created by Aaron Boodman (now of Greasemonkey and Google Gears fame)

labels.js

Page 15: Unobtrusive JavaScript with jQuery
Page 16: Unobtrusive JavaScript with jQuery
Page 17: Unobtrusive JavaScript with jQuery

• 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

<label for="search">Search</label><input type="text" id="search" name="q">

How it works

Page 18: Unobtrusive JavaScript with jQuery

• 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

Django filter lists

Page 19: Unobtrusive JavaScript with jQuery
Page 20: Unobtrusive JavaScript with jQuery
Page 21: Unobtrusive JavaScript with jQuery

http://www.neighbourhoodfixit.com/

Page 22: Unobtrusive JavaScript with jQuery
Page 23: Unobtrusive JavaScript with jQuery
Page 24: Unobtrusive JavaScript with jQuery

Implementing Terms and Conditions

Page 25: Unobtrusive JavaScript with jQuery

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

Bad

Page 26: Unobtrusive JavaScript with jQuery

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

Also bad

Page 27: Unobtrusive JavaScript with jQuery

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>?

Better

Page 28: Unobtrusive JavaScript with jQuery

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>?

Better

Page 29: Unobtrusive JavaScript with jQuery

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

Best

Page 30: Unobtrusive JavaScript with jQuery

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

Page 31: Unobtrusive JavaScript with jQuery

• 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

JavaScript for sidenotes

Page 32: Unobtrusive JavaScript with jQuery

With JavaScriptwindow.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; } } }}

Page 33: Unobtrusive JavaScript with jQuery

With JavaScriptwindow.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; } } }}

Page 34: Unobtrusive JavaScript with jQuery

With JavaScriptwindow.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; } } }}

Page 35: Unobtrusive JavaScript with jQuery

With JavaScriptwindow.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; } } }}

Page 36: Unobtrusive JavaScript with jQuery

Problems

Page 37: Unobtrusive JavaScript with jQuery

• 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

Problems

Page 38: Unobtrusive JavaScript with jQuery

• 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

Problems

Page 39: Unobtrusive JavaScript with jQuery

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; });});

Page 40: Unobtrusive JavaScript with jQuery

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; });});

Page 41: Unobtrusive JavaScript with jQuery

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; });});

Page 42: Unobtrusive JavaScript with jQuery

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; });});

Page 43: Unobtrusive JavaScript with jQuery

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; });});

Page 44: Unobtrusive JavaScript with jQuery

With jQuery

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

Page 45: Unobtrusive JavaScript with jQuery

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; });});

Page 46: Unobtrusive JavaScript with jQuery

• 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

Advantages

Page 47: Unobtrusive JavaScript with jQuery

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

Page 48: Unobtrusive JavaScript with jQuery

jQuery characteristics

Page 49: Unobtrusive JavaScript with jQuery

jQuery characteristics

• Minimal namespace impact (one symbol)

Page 50: Unobtrusive JavaScript with jQuery

jQuery characteristics

• Minimal namespace impact (one symbol)

• Focus on the interaction between JavaScript and HTML

Page 51: Unobtrusive JavaScript with jQuery

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

Page 52: Unobtrusive JavaScript with jQuery

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

Page 53: Unobtrusive JavaScript with jQuery

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

Page 54: Unobtrusive JavaScript with jQuery

Essential tools

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

Page 55: Unobtrusive JavaScript with jQuery

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

Page 56: Unobtrusive JavaScript with jQuery

jQuery('#nav')

jQuery('div#intro h2')

jQuery('#nav li.current a')

CSS selectors

Page 57: Unobtrusive JavaScript with jQuery

$('#nav')

$('div#intro h2')

$('#nav li.current a')

CSS selectors

Page 58: Unobtrusive JavaScript with jQuery

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)

Page 59: Unobtrusive JavaScript with jQuery

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

Page 60: Unobtrusive JavaScript with jQuery

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]

Page 61: Unobtrusive JavaScript with jQuery

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);

});

Page 62: Unobtrusive JavaScript with jQuery

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);

});

Page 63: Unobtrusive JavaScript with jQuery

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);

});

Page 64: Unobtrusive JavaScript with jQuery

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

Page 65: Unobtrusive JavaScript with jQuery

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

Page 66: Unobtrusive JavaScript with jQuery

Introspectors

• $('div:first').attr('title')

• $('div:first').html()

• $('div:first').text()

• $('div:first').css('color')

• $('div:first').is('.entry')

Page 67: Unobtrusive JavaScript with jQuery

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')

Page 68: Unobtrusive JavaScript with jQuery

Bulk modifiers

• $('a:first').attr({'title': 'First link on the page','href': 'http://2008.xtech.org/'

});

• $('a:first').css({'color': 'red','backgroundColor': 'blue'

});

Page 69: Unobtrusive JavaScript with jQuery

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

Page 70: Unobtrusive JavaScript with jQuery

Style modifiers

• $(selector).css(...)

• $(selector).addClass(class)

• $(selector).removeClass(class)

• $(selector).hasClass(class)

• $(selector).toggleClass(class)

Page 71: Unobtrusive JavaScript with jQuery

Dimensions

• $(selector).height()

• $(selector).height(200)

• $(selector).width()

• $(selector).width(200)

• var offset = $(selector).offset()

• offset.top, offset.left

Page 72: Unobtrusive JavaScript with jQuery

Navigators - finding

• $('h1').add('h2')

• $('div:first').find('a')

• $('a:first').children()

• $('a:first').children('em')

• $('a').parent()

• $('a:first').parents()

• $('a:first').siblings()

• $('h3').next()

• $('h3:first').nextAll()

• $('h3').prev()

• $('h3').prevAll()

• $('a:first').contents()

Page 73: Unobtrusive JavaScript with jQuery

Navigators - filtering• $('div').eq(1) // gets second

• $('div').filter('.entry')

• $('div').filter(function(i) {

return this.className == 'foo'

}

• $('div').not('.entry')

• $('div').slice(1, 3) // 2nd,3rd

• $('div').slice(-1) // last

Page 74: Unobtrusive JavaScript with jQuery

DOM modifiers• els.append(content)

• content.appendTo(els)

• els.prepend(content)

• content.prependTo(els)

• els.after(content)

• els.before(content)

• content.insertAfter(els)

• content.insertBefore(els)

• els.wrapAll('<div /'>)

• els.wrapInner('<div /'>)

• els.empty()

• els.remove()

Page 75: Unobtrusive JavaScript with jQuery

DOM construction

• var p = $('<p id="foo" />').addClass('bar');

• p.text('This is some text').css('color', 'red');

• p.appendTo(document.body);

Page 76: Unobtrusive JavaScript with jQuery

jQuery and Microformats

Page 77: Unobtrusive JavaScript with jQuery

Favourite restaurant list

Page 78: Unobtrusive JavaScript with jQuery

With JavaScript enabled

Page 79: Unobtrusive JavaScript with jQuery

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> ...

Page 80: Unobtrusive JavaScript with jQuery

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 });

Page 81: Unobtrusive JavaScript with jQuery

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 });

Page 82: Unobtrusive JavaScript with jQuery

Displaying the map

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

Page 83: Unobtrusive JavaScript with jQuery

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); });

Page 84: Unobtrusive JavaScript with jQuery

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); });

Page 85: Unobtrusive JavaScript with jQuery

Events

$('a:first').bind('click', function() {

$(this).css('backgroundColor' ,'red');

return false;

});

Page 86: Unobtrusive JavaScript with jQuery

Events

$('a:first').click(function() {

$(this).css('backgroundColor' ,'red');

return false;

});

Page 87: Unobtrusive JavaScript with jQuery

Event objects

$('a:first').click(function(ev) {

$(this).css('backgroundColor' ,'red');

ev.preventDefault();

});

Page 88: Unobtrusive JavaScript with jQuery

Triggering events

$('a:first').trigger('click');

Page 89: Unobtrusive JavaScript with jQuery

Triggering events

$('a:first').click();

Page 90: Unobtrusive JavaScript with jQuery

Events• blur()

• change()

• click()

• dblclick()

• error()

• focus()

• keydown()

• keypress()

• keyup()

• load()

• mousedown()

• mouseover()

• mouseup()

• resize()

• scroll()

• select()

• submit()

• unload()

Page 91: Unobtrusive JavaScript with jQuery

labels.js with jQuery

...<label class="inputHint" for="email">E-mail:</label><input id="email" type="text">...

Page 92: Unobtrusive JavaScript with jQuery

labels.js with jQueryjQuery(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); });});

Page 93: Unobtrusive JavaScript with jQuery

labels.js with jQueryjQuery(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); });});

Page 94: Unobtrusive JavaScript with jQuery

labels.js with jQueryjQuery(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); });});

Page 95: Unobtrusive JavaScript with jQuery

labels.js with jQueryjQuery(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); });});

Page 96: Unobtrusive JavaScript with jQuery

labels.js with jQueryjQuery(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); });});

Page 97: Unobtrusive JavaScript with jQuery

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').end();

Page 98: Unobtrusive JavaScript with jQuery

Inline form help

Page 99: Unobtrusive JavaScript with jQuery

With JavaScript enabled

Page 100: Unobtrusive JavaScript with jQuery

Form help HTML

<div class="helpWrapper"> <div class="fieldWrapper"> <label for="email">E-mail address</label> <input id="email" type="text"> </div> <p class="helpText"> We promise not to spam you! </p> </div>

Page 101: Unobtrusive JavaScript with jQuery

Form help JavaScript

jQuery(function($) { // Set up contextual help... var contextualHelp = $('<div id="contextualHelp"></div>'); contextualHelp.hide().appendTo(document.body);

Page 102: Unobtrusive JavaScript with jQuery

Form help JavaScript

// helpArrow is a div containing the ]- thing var helpArrow = $('<div id="helpArrow"></div>').css({ 'position': 'absolute', 'left': '450px', 'top': '0px', // This changes 'height': '22px', // This changes 'width': '20px' }).hide().appendTo(document.body);

Page 103: Unobtrusive JavaScript with jQuery

Form help JavaScript

var helpBrace = $('<div id="helpArrowBrace"></div>').css({ 'width': '5px', 'height': '100%', 'border-right': '2px solid #ccc', 'border-bottom': '2px solid #ccc', 'border-top': '2px solid #ccc' }).appendTo(helpArrow);

Page 104: Unobtrusive JavaScript with jQuery

Form help JavaScript

var helpBrace = $('<div id="helpArrowBrace"></div>').css({ 'width': '5px', 'height': '100%', 'border-right': '2px solid #ccc', 'border-bottom': '2px solid #ccc', 'border-top': '2px solid #ccc' }).appendTo(helpArrow);

Page 105: Unobtrusive JavaScript with jQuery

Form help JavaScript

var helpBar = $('<div id="helpArrowBar"></div>').css({ 'width': '15px', 'height': '0px', 'border-top': '2px solid #ccc', 'position': 'absolute', 'top': '50%', 'left': '5px' }).appendTo(helpArrow);

Page 106: Unobtrusive JavaScript with jQuery

Form help JavaScript

function showHelp(helpWrapper, helpHtml) { // Display contextual help next to helpWrapper div var top = $(helpWrapper).offset().top; helpArrow.css('top', top + 'px'); helpArrow.height($(helpWrapper).height()).show(); contextualHelp.css('top', top + 'px').show().html(helpHtml); }

Page 107: Unobtrusive JavaScript with jQuery

Form help JavaScript

function showHelpForField(field) { var helpWrapper = input.parents('div.helpWrapper'); var helpHtml = helpWrapper.find('p.helpText').html(); showHelp(helpWrapper, helpHtml); }

Page 108: Unobtrusive JavaScript with jQuery

Form help JavaScript

$('div.helpWrapper').find(':input').focus(function() { showHelpForField(this); }).end().find('p.helpText').hide();

Page 109: Unobtrusive JavaScript with jQuery

Advanced Events

$('a:first').unbind('click');

$('a:first').unbind();

$('a:first').one('click', function() {

// executes the first time the link is clicked

}

$('a:first').toggle(func1, func2);

$('a:first').hover(func1, func2);

Page 110: Unobtrusive JavaScript with jQuery

Custom events

function updateInbox(event, mail) {

alert('New e-mail: ' + mail);

}

$(window).bind('mail-recieved', updateInbox)

$(window).bind('mail-recieved', soundPing)

$(window).trigger('mail-recieved', [mail])

Page 111: Unobtrusive JavaScript with jQuery

Ajax• Simple:

• jQuery('div#news').load('/news.html');

• Complex:

• jQuery.ajax(options) - low level control

• jQuery.get(url, [data], [callback])

• jQuery.post(url, [data], [callback], [type])

• jQuery.getJSON(url, [data], [callback])

• jQuery.getScript(url, [data], [callback])

Page 112: Unobtrusive JavaScript with jQuery

Ajax global events

• .ajaxComplete(function() { })

• .ajaxError(function() { })

• .ajaxSend(function() { })

• .ajaxStart(function() { })

• .ajaxStop(function() { })

• .ajaxSuccess(function() { })

Page 113: Unobtrusive JavaScript with jQuery

Ajax sidenote

$('a.sidenote').one('click', function() { $('<p />').load(this.href).appendTo(document.body); // Make the link stop being a link $(this).replaceWith($(this).contents()); return false;});

Page 114: Unobtrusive JavaScript with jQuery

Loading...

var loading = $( '<img alt="loading" src="loading.gif" />').appendTo(document.body).hide()

jQuery(window).ajaxStart(function() { loading.show() });jQuery(window).ajaxStop(function() { loading.hide() });

Page 115: Unobtrusive JavaScript with jQuery

jQuery(xml)var signup = $('div#en_sidebar div#signup.rhsbox');var news_box = $('<div id="news_feed" class="rhsbox"></div>');news_box.html(signup.html());news_box.find('div.box').empty();var ul = $('<ul />');var feed_url = jQuery('link[type=application/atom+xml]').attr('href');jQuery.get(feed_url, function(xml) { var feed = jQuery(xml); feed.find('feed entry').each(function() { var title = $(this).find('title').text(); var link = $(this).find('link').attr('href'); var li = $('<li><a href="' + link + '">' + title + '</a></li>'); li.appendTo(ul); });});ul.css('text-align', 'left').appendTo(news_box.find('div.box'));news_box.insertBefore(signup);

Paste the above in to Firebug on any 2008.xtech.org page

Page 116: Unobtrusive JavaScript with jQuery

Animation

• jQuery has built in effects: $('h1').hide('slow');

$('h1').slideDown('fast');

$('h1').fadeOut(2000);

• Chaining automatically queues the effects: $('h1').fadeOut(1000).slideDown()

Page 117: Unobtrusive JavaScript with jQuery

$("#block").animate({

width: "+=60px",

opacity: 0.4,

fontSize: "3em",

borderWidth: "10px"

}, 1500);

You can roll your own

Page 118: Unobtrusive JavaScript with jQuery

A login form that shakes its head

Page 119: Unobtrusive JavaScript with jQuery

The shake animationfunction shake(el, callback) { el.css({'position': 'relative'}); el.animate( {left: '-10px'}, 100 ).animate( {left: '+10px'}, 100 ).animate( {left: '-10px'}, 100 ).animate( {left: '+10px'}, 100 ).animate( {left: '0px'}, 100, callback );};

Page 120: Unobtrusive JavaScript with jQuery

The PHP$username = isset($_POST['username']) ? $_POST['username'] : '';$password = isset($_POST['password']) ? $_POST['password'] : '';

$msg = '';if ($_POST) { if ($username == 'simon' && $password == 'xtech') { if (is_xhr()) { json_response(array('ok' => true, 'redirect' => 'loggedin.php')); } else { header('Location: loggedin.php'); } exit; } if (is_xhr()) { json_response(array('ok' => false)); } $msg = '<p class="error">Incorrect username or password.</p>';}

Page 121: Unobtrusive JavaScript with jQuery

PHP utility functions

function is_xhr() { return (isset($_SERVER["HTTP_X_REQUESTED_WITH"]) && $_SERVER["HTTP_X_REQUESTED_WITH"] == 'XMLHttpRequest');}

function json_response($obj) { header('Content-Type: application/json'); print json_encode($obj); exit;}

Page 122: Unobtrusive JavaScript with jQuery

The JavaScript

jQuery(function($) { $('#username').focus(); $('form').submit(function() { var form = $(this); var url = form.attr('action'); var data = form.serialize(); jQuery.post(url, data, function(json) { if (json.ok) { window.location = json.redirect; } else { ...

Page 123: Unobtrusive JavaScript with jQuery

The JavaScript

else { $('#password').val('').focus(); shake(form, function() { if (!form.find('p.error').length) { $('<p class="error">Incorrect username or ' + 'password.</p>').prependTo(form). hide().slideDown(); } }); } }, 'json'); // jQuery.post(url, data, callback, type); return false; });});

Page 124: Unobtrusive JavaScript with jQuery

shake() as a pluginjQuery.fn.shake = function(callback) { this.css({'position': 'relative'}); return this.animate( {left: '-10px'}, 100 ).animate( {left: '+10px'}, 100 ).animate( {left: '-10px'}, 100 ).animate( {left: '+10px'}, 100 ).animate( {left: '0px'}, 100, callback );};$('form').shake();$('form').shake(function() { alert('shaken!') });

Page 125: Unobtrusive JavaScript with jQuery

jQuery Plugins

Page 126: Unobtrusive JavaScript with jQuery

Plugins

• jQuery is extensible through plugins, which can add new methods to the jQuery object

• Form: better form manipulation

• UI: drag and drop and widgets

• $('img[@src$=.png]').ifixpng()

• ... many dozens more

Page 127: Unobtrusive JavaScript with jQuery

Logging the chain

jQuery.fn.log = function(message) { if (message) { console.log(message, this); } else { console.log(this); } return this;};

Page 128: Unobtrusive JavaScript with jQuery

jQuery.fn.hideLinks = function() {

this.find('a[href]').hide();

return this;

}

$('p').hideLinks();

Page 129: Unobtrusive JavaScript with jQuery

jQuery.fn.hideLinks = function() {

this.find('a[href]').hide();

return this;

}

$('p').hideLinks();

Page 130: Unobtrusive JavaScript with jQuery

jQuery.fn.hideLinks = function() {

return this.find('a[href]').hide().end();

}

$('p').hideLinks();

Page 131: Unobtrusive JavaScript with jQuery

jQuery.expr[':'].second = function(a,i){return i==1;}

Extending the selector engine

$('div:second') - the second div on the page

Page 132: Unobtrusive JavaScript with jQuery

jQuery data()

• Attaching data directly to DOM nodes can create circular references and cause memory leaks

• jQuery provides a data() method for safely attaching information

• $('div:first').data('key', 'value');

• var value = $('div:first').data('key');

Page 133: Unobtrusive JavaScript with jQuery

jQuery utilities

• jQuery.each(object, callback)

• jQuery.extend(target, object)

• jQuery.grep(array, callback)

• jQuery.map(array, callback)

• jQuery.inArray(value, array)

• jQuery.unique(array)

• jQuery.makeArray(obj)

• jQuery.isFunction(obj)

• jQuery.trim(string)