jarkko laine

87
Accessible Ajax on Rails Jarkko Laine with Geoffrey Grosenbach

Upload: sampetruda

Post on 29-Nov-2014

952 views

Category:

Documents


0 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Jarkko Laine

Accessible Ajax on Rails

Jarkko Lainewith Geoffrey Grosenbach

Page 2: Jarkko Laine
Page 3: Jarkko Laine

r.resources :categories do |cat| cat.resources :products cat.resources :companies cat.resources :subcategories do |sub| sub.resources :products sub.resources :companies end end

Page 4: Jarkko Laine

<li id=“undone_1”><input id=“undone_box_1” name=“item[1][done]” type=“checkbox” value=“1” /><input name=“item[1][done]” type=“hidden” value=“0” /><label for=“undone_box_1”> Buy carrots</label><script type=“text/javascript”>//<![CDATA[new Form.Element.EventObserver(‘undone_box_1’, function(element, value) { new Ajax.Request(‘/items/1’, {asynchronous:true, evalScripts:true, method:‘put’, parameters:value + ‘&authenticity_token=’ + encodeURIComponent(‘8d829cfcccdf4d2b494891ef47cc95893faa361e’)})})//]]></script></li><li id=“undone_2”><input id=“undone_box_2” name=“item[2][done]” type=“checkbox” value=“1” /><input name=“item[2][done]” type=“hidden” value=“0” /><label for=“undone_box_2”> Return bottles to recycling</label><script type=“text/javascript”>//<![CDATA[new Form.Element.EventObserver(‘undone_box_2’, function(element, value) { new Ajax.Request(‘/items/2’, {asynchronous:true, evalScripts:true, method:‘put’, parameters:value + ‘&authenticity_token=’ + encodeURIComponent(‘8d829cfcccdf4d2b494891ef47cc95893faa361e’)})})//]]></script></li><li id=“undone_3”><input id=“undone_box_3” name=“item[3][done]” type=“checkbox” value=“1” /><input name=“item[3][done]” type=“hidden” value=“0” /><label for=“undone_box_3”> Return bottles to recycling</label><script type=“text/javascript”>//<![CDATA[new Form.Element.EventObserver(‘undone_box_3’, function(element, value) { new Ajax.Request(‘/items/3’, {asynchronous:true, evalScripts:true, method:‘put’, parameters:value + ‘&authenticity_token=’ + encodeURIComponent(‘8d829cfcccdf4d2b494891ef47cc95893faa361e’)})})//]]></script></li>

Page 5: Jarkko Laine

Accessibility“the degree to

which a product (e.g., device, service,

environment) is accessible by as many people as

possible.”-Wikipedia

Page 6: Jarkko Laine

AccessibilityThe ultimate goal

Page 7: Jarkko Laine

ProgressiveEnhancement

A methodology for producing accessible web content

Page 8: Jarkko Laine

ProgressiveEnhancement

Roots: graceful degradation

hardly ever happened in real world

Page 9: Jarkko Laine

Progressive enhancement turns graceful

degradation on its head build the essential, universal

first

then gradually enhance the experience for those capable of

digesting it

Page 10: Jarkko Laine

Unobtrusive javascript

part of the progressive enhancement process

Page 11: Jarkko Laine

Unobtrusive javascript

Separation of concerns

Page 12: Jarkko Laine

<div id=“wrapper”> <ul id=“my_stuff”> <li id=“hide_me”> I am Iron Man </li> </ul></div>

#wrapper { width: 100%; overflow: hidden;}#my_stuff { list-style: none;}#my_stuff li { padding: 1.5em;}

Structure

Presentation

Page 13: Jarkko Laine

<div id=“wrapper”> <ul id=“my_stuff”> <li id=“hide_me”> I am Iron Man </li> </ul></div>

#wrapper { width: 100%; overflow: hidden;}#my_stuff { list-style: none;}#my_stuff li { padding: 1.5em;}

Structure

Presentationdocument.observe(‘dom:loaded’, function() { $(‘hide_me’).hide(); $(‘my_stuff’).observe(‘click’, function() { // do something })});

Behaviour

Page 14: Jarkko Laine

Unobtrusive javascript

clean and maintainable code—just like MVC in Rails

Benefits

Page 15: Jarkko Laine

Unobtrusive javascript

fits naturally in the progressive enhancement process

Benefits

Page 16: Jarkko Laine

Unobtrusive javascript

makes it easy for designers and programmers to work on

the same code base

Benefits

Page 17: Jarkko Laine

Only (part of) a means

BUT

Page 18: Jarkko Laine

Does not guarantee accessibility

BUT

Page 19: Jarkko Laine

<a href=“#”>Get bacon!</a>

$(‘mylink’).observe(‘click’, function(e) { window.location = ‘http://google.fi‘;});

Page 20: Jarkko Laine

<a href=“#”>Get bacon!</a>

$(‘mylink’).observe(‘click’, function(e) { window.location = ‘http://google.fi‘;});

Fully unobtrusive

Page 21: Jarkko Laine

Not accessible at all

Page 22: Jarkko Laine

JavaScript on RailsBrief History of

Page 23: Jarkko Laine

JavaScript on RailsBrief History of

link_to_remoteform_remote_tag

Page 24: Jarkko Laine

JavaScript on RailsBrief History of

link_to_remoteform_remote_tag

absolutely fabulous

Page 25: Jarkko Laine

BUT...

Page 26: Jarkko Laine

2 problems

Page 27: Jarkko Laine

2 problems<form action=“/items” id=“add_form” method=“post” onsubmit=“new Ajax.Request(‘/items’, {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;” style=“display: none;”>

Page 28: Jarkko Laine

2 problems<a href=“#” onclick=“$(‘add_form’).toggle(); return false;”> Add new item</a>

Page 29: Jarkko Laine

Dan Webb and Luke Redpath

made Rails js helpers unobtrusive

provided a method for attaching behaviours to elements

UJS4Railsreaction:

Page 30: Jarkko Laine

UJS4Railsreaction:

heavy

didn’t encourage people towards progressive enhancement

Page 31: Jarkko Laine

Low Protake two:

underlying JS framework behind UJS4Rails

better to just use Low Pro than to mess with the Rails internals

Page 32: Jarkko Laine

Low ProEvent.addBehaviour()

Behaviour “classes”

DOM builder

Page 33: Jarkko Laine

Event.addBehavior({ ‘#add_form‘ : function() { this.hide(); }, ‘#add_new_link:click‘ : function(e) { $(‘add_form’).toggle(); e.stop(); }});

Page 34: Jarkko Laine

Event.addBehavior({ ‘#add_form’ : Remote.Form});

Behaviours

Page 35: Jarkko Laine

Event.addBehavior({ ‘#add_form’ : Remote.Form({ onComplete: doSomething })});

Behaviours

Page 36: Jarkko Laine

DOM Builder$div({ id: ‘run-1’}, $p(‘A’, $em(“marathon”), ‘!’));

<div id=“run-1”> <p>A<em>marathon</em>!</p></div>

Page 37: Jarkko Laine

The bastard sonvar item = DOM.Builder.fromHTML( ‘<li>Remember to recover!</li>’);list.append(item);

Page 38: Jarkko Laine

CODE!

Page 39: Jarkko Laine

Event Delegation

Page 40: Jarkko Laine

The problems

event handlers aren’t automatically assigned to dynamically added

elements

performance inversely related to the amount of elements

Page 41: Jarkko Laine

The problemsEvent.addBehavior({ ‘td:click‘ : function(e) { // execute some code for table cells }});

What if there are thousands of cells?

Page 42: Jarkko Laine

The solution lies in event propagation

Page 43: Jarkko Laine

table

tr

td *click*

*click*

*click*

Page 44: Jarkko Laine

Two event propagation modes

event captureevent bubbling

Page 45: Jarkko Laine

Event.addBehavior({ ‘table:click‘ : function(e) { // do something upon a table click }, ‘td:click‘ : function(e) { // execute some code for table cells }});

Page 46: Jarkko Laine

table

tr

td *click*

*click*

*click*

event capture

Page 47: Jarkko Laine

event bubbling

table

tr

td *click*

*click*

*click*

Page 48: Jarkko Laine

current W3C DOM spec supports both modes

Page 49: Jarkko Laine

as do all modern browsers

Page 50: Jarkko Laine

alas, not Internet Explorer

Page 51: Jarkko Laine

and thus, not Prototype either

Page 52: Jarkko Laine

so we’ll rely on event bubbling

(which is totally fine)

Page 53: Jarkko Laine

So What?

given that every event knows its target element (Event.element())

why not observe a higher-level element and only then work

according to the original target?

Page 54: Jarkko Laine

Event Delegationenter

made "famous" by Christian Heilmann with Yahoo! UI

now "natively" supported by Low Pro, with Event.delegate()

Page 55: Jarkko Laine

Event.addBehavior({ ‘table:click’ : Event.delegate({ ‘td‘ : function(e) { var el = e.element(); // ... }, ‘a‘ : function(e) { e.stop(); // handle link clicks } })});

Page 56: Jarkko Laine

CODE!

Page 57: Jarkko Laine

Caveatswith event delegation

not all events bubble up (most notably focus and blur)

can kill performance in some cases (onmousemove)

Page 58: Jarkko Laine

Low Pro Behaviours

Page 59: Jarkko Laine

Low Pro Behaviours“Behaviours are an object orientated

mechanism by which you can handle events and maintain the state of an element”

-Dan Webb

Page 60: Jarkko Laine

var myForm = $(‘add_form’);var ajaxForm = new Remote.Form( myForm, { method: ‘post’ } );

Page 61: Jarkko Laine

Event.addBehavior({ ‘#add_form’: Remote.Form, ‘#ajax_link’ : Remote.Link});

Page 62: Jarkko Laine

Event.addBehavior({ ‘#add_form’: Remote.Form({ method : ‘put’ })});

Page 63: Jarkko Laine

Behaviours bundled with Low Pro

Remote.Form, Remote.Link

Drag’n’drop

Auto-completer

In-place editor

Page 64: Jarkko Laine

...but the real benefits come from

Page 65: Jarkko Laine

...but the real benefits come from

writing your own Behaviours to structure your

JavaScript code

Page 66: Jarkko Laine

var Hover = Behavior.create();

Page 67: Jarkko Laine

var Hover = Behavior.create();

Object.extend(Hover.prototype, { initialize: function(className) { this.className = className || ‘over’; }, onmouseover: function() { this.element.addClassName(this.className); }, onmouseout: function() { this.element.removeClassName(this.className); }});

Page 68: Jarkko Laine

var Hover = Behavior.create({ initialize: function(className) { this.className = className || ‘over’; }, onmouseover: function() { this.element.addClassName(this.className); }, onmouseout: function() { this.element.removeClassName(this.className); }});

Page 69: Jarkko Laine

Event.addBehavior({ ‘.dongle’: Hover(‘hover’)});

Page 70: Jarkko Laine

Event delegation with Behaviours

Page 71: Jarkko Laine

var Calendar = Behavior.create({ // other methods hidden for clarity onclick : function(e) { var source = e.element(); e.stop();

if ($(source.parentNode).hasClassName(‘day’)) return this._setDate(source); if ($(source.parentNode).hasClassName(‘back’)) return this._backMonth(); if ($(source.parentNode).hasClassName(‘forward’)) return this._forwardMonth(); }});

Page 72: Jarkko Laine

var Calendar = Behavior.create({ // other methods hidden for clarity onclick : Event.delegate({ ‘.day a‘ : function(e) { // set date }, ‘back a‘ : function(e) { // go back a month }, ‘.forward a‘ : function(e) { // go forward } })});

Page 73: Jarkko Laine

Linking behaviours in more complex

situations

Page 74: Jarkko Laine

Draggable = Behavior.create({ initialize : function(options) { // code hidden for clarity this.handle = this.options.handle || this.element; Draggable.Handle.attach(this.handle, this); } // code hidden for clarity});

Draggable.Handle = Behavior.create({ initialize : function(draggable) { this.draggable = draggable; }, onmousedown : function(e) { // code hidden for clarity }});

Page 75: Jarkko Laine

CODE!

Page 76: Jarkko Laine

var TodoList = Behavior.create({ initialize: function() { // initialization code }, onclick: function() { // do something upon a click }});

Page 77: Jarkko Laine

jQuery

Page 78: Jarkko Laine

jQuery$(document).ready(function() { $(“a”).click(function() { alert(“Hello world!”); });});

Page 79: Jarkko Laine

jQuery.fn.check = function() { return this.each(function() { this.checked = true; });};

$(“input[@type=’checkbox’]”).check();

Page 80: Jarkko Laine

Low Pro for jQuery

Page 81: Jarkko Laine

Hover = $.klass({ initialize: function(hoverClass) { this.hoverClass = hoverClass; }, onmouseover: function() { this.element.addClass(this.hoverClass); }, onmouseout: function() { this.element.removeClass(this.hoverClass); }});

jQuery(function($) { $(‘span.name’).attach(Hover, ‘myClassName’);});

Page 82: Jarkko Laine

Even event delegation!

Page 83: Jarkko Laine

$(‘#thing’).click($.delegate({ ‘.quit‘: function() { /* do quit stuff */ }, ‘.edit‘: function() { /* do edit stuff */ }}));

Page 84: Jarkko Laine

DateSelector = $.klass({ onclick: $.delegate({ ‘.close‘: function() { this.close(); }, ‘.day‘: function(e) { this.selectDate(e.target); } }), selectDate: function(dayElement) { // code ... }, close: function() { // code ... }});

Page 85: Jarkko Laine

?

Page 86: Jarkko Laine

Thank You

Page 87: Jarkko Laine

UnobtrusiveProtot!peb! J"rkko L"ine

!"

Robust, Org!nized J!v!scripting

Use code “UNOBTRUSIVE” in Google Checkout to get 50% off of any ebook

or screencast