accessible ajax on rails
play

Accessible Ajax on Rails Jarkko Laine with Geoffrey Grosenbach - PowerPoint PPT Presentation

Accessible Ajax on Rails Jarkko Laine with Geoffrey Grosenbach r.resources :categories do |cat| cat.resources :products cat.resources :companies cat.resources :subcategories do |sub| sub.resources :products sub.resources :companies end


  1. Accessible Ajax on Rails Jarkko Laine with Geoffrey Grosenbach

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

  3. //<![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’)})}) //]]>

  4. Accessibility “the degree to which a product (e.g., device, service, environment) is accessible by as many people as possible.” -Wikipedia

  5. Accessibility The ultimate goal

  6. Progressive Enhancement A methodology for producing accessible web content

  7. Progressive Enhancement Roots: graceful degradation hardly ever happened in real world

  8. Progressive enhancement turns graceful degradation on its head build the essential, universal first then gradually enhance the experience for those capable of digesting it

  9. Unobtrusive javascript part of the progressive enhancement process

  10. Unobtrusive javascript Separation of concerns

  11. Structure <div id=“wrapper”> <ul id=“my_stuff”> <li id=“hide_me”> I am Iron Man </li> </ul> </div> Presentation #wrapper { width: 100%; overflow: hidden; } #my_stuff { list-style: none; } #my_stuff li { padding: 1.5em; }

  12. Structure <div id=“wrapper”> <ul id=“my_stuff”> <li id=“hide_me”> Behaviour I am Iron Man </li> </ul> </div> document.observe(‘dom:loaded’, function() { $(‘hide_me’).hide(); $(‘my_stuff’).observe(‘click’, Presentation function() { // do something }) }); #wrapper { width: 100%; overflow: hidden; } #my_stuff { list-style: none; } #my_stuff li { padding: 1.5em; }

  13. Unobtrusive javascript Benefits clean and maintainable code —just like MVC in Rails

  14. Unobtrusive javascript Benefits fits naturally in the progressive enhancement process

  15. Unobtrusive javascript Benefits makes it easy for designers and programmers to work on the same code base

  16. BUT Only (part of) a means

  17. BUT Does not guarantee accessibility

  18. <a href=“#”>Get bacon!</a> $(‘mylink’).observe(‘click’, function(e) { window.location = ‘http://google.fi‘; });

  19. <a href=“#”>Get bacon!</a> $(‘mylink’).observe(‘click’, function(e) { window.location = ‘http://google.fi‘; }); Fully unobtrusive

  20. Not accessible at all

  21. Brief History of JavaScript on Rails

  22. Brief History of JavaScript on Rails link_to_remote form_remote_tag

  23. Brief History of JavaScript on Rails link_to_remote form_remote_tag absolutely fabulous

  24. BUT...

  25. 2 problems

  26. 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;”>

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

  28. reaction: UJS4Rails Dan Webb and Luke Redpath made Rails js helpers unobtrusive provided a method for attaching behaviours to elements

  29. reaction: UJS4Rails heavy didn’t encourage people towards progressive enhancement

  30. take two: Low Pro underlying JS framework behind UJS4Rails better to just use Low Pro than to mess with the Rails internals

  31. Low Pro Event.addBehaviour() Behaviour “classes” DOM builder

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

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

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

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

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

  37. CODE!

  38. Event Delegation

  39. The problems event handlers aren’t automatically assigned to dynamically added elements performance inversely related to the amount of elements

  40. The problems Event.addBehavior({ ‘td:click‘ : function(e) { // execute some code for table cells } }); What if there are thousands of cells?

  41. The solution lies in event propagation

  42. *click* table *click* tr *click* td

  43. Two event propagation modes event capture event bubbling

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

  45. event capture *click* table *click* tr *click* td

  46. event bubbling *click* table *click* tr *click* td

  47. current W3C DOM spec supports both modes

  48. as do all modern browsers

  49. alas, not Internet Explorer

  50. and thus, not Prototype either

  51. so we’ll rely on event bubbling (which is totally fine)

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

  53. enter Event Delegation made "famous" by Christian Heilmann with Yahoo! UI now "natively" supported by Low Pro, with Event.delegate()

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

  55. CODE!

  56. Caveats with event delegation not all events bubble up (most notably focus and blur) can kill performance in some cases (onmousemove)

  57. Low Pro Behaviours

  58. Low Pro Behaviours “Behaviours are an object orientated mechanism by which you can handle events and maintain the state of an element” -Dan Webb

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

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

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

  62. Behaviours bundled with Low Pro Remote.Form, Remote.Link Drag’n’drop Auto-completer In-place editor

  63. ...but the real benefits come from

  64. ...but the real benefits come from writing your own Behaviours to structure your JavaScript code

  65. var Hover = Behavior.create();

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

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

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

  69. Event delegation with Behaviours

Recommend


More recommend