Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
620
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
5
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 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”> Behaviour <li id=“hide_me”> I am Iron Man </li> </ul> </div> document.observe(‘dom:loaded’, function() { $(‘hide_me’).hide(); Presentation $(‘my_stuff’).observe(‘click’, 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. table *click* tr *click* td *click*
  • 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 table *click* tr *click* td *click*
  • 46. event bubbling table *click* tr *click* td *click*
  • 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
  • 70. 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(); } });
  • 71. 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 } }) });
  • 72. Linking behaviours in more complex situations
  • 73. 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 } });
  • 74. CODE!
  • 75. var TodoList = Behavior.create({ initialize: function() { // initialization code }, onclick: function() { // do something upon a click } });
  • 76. jQuery
  • 77. jQuery $(document).ready(function() { $(“a”).click(function() { alert(“Hello world!”); }); });
  • 78. jQuery.fn.check = function() { return this.each(function() { this.checked = true; }); }; $(“input[@type=’checkbox’]”).check();
  • 79. Low Pro for jQuery
  • 80. 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’); });
  • 81. Even event delegation!
  • 82. $(‘#thing’).click($.delegate({ ‘.quit‘: function() { /* do quit stuff */ }, ‘.edit‘: function() { /* do edit stuff */ } }));
  • 83. DateSelector = $.klass({ onclick: $.delegate({ ‘.close‘: function() { this.close(); }, ‘.day‘: function(e) { this.selectDate(e.target); } }), selectDate: function(dayElement) { // code ... }, close: function() { // code ... } });
  • 84. ?
  • 85. Thank You
  • 86. Unobtrusive Protot pe Robust, Org nized J v scripting b J rkko L ine Use code “UNOBTRUSIVE” in Google Checkout to get 50% off of any ebook or screencast