JavaScript and DOM Pattern Implementation


Published on

Covers implementation details of five important Ajax design patterns in terms of DOM, CSS and JavaScript.

Published in: Business, Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide
  • JavaScript and DOM Pattern Implementation

    1. 1. JavaScript DOM Pattern Implementations Dave Johnson [email_address]
    2. 2. Agenda <ul><li>About me </li></ul><ul><li>Patterns Overview </li></ul><ul><li>Patterns </li></ul><ul><ul><li>Inline Editing </li></ul></ul><ul><ul><li>Composite Controls </li></ul></ul><ul><ul><li>Popup </li></ul></ul><ul><ul><li>Copy and Paste </li></ul></ul><ul><ul><li>Live Scrolling </li></ul></ul>
    3. 3. About <ul><li>Author Enterprise Ajax </li></ul><ul><li> </li></ul>
    4. 4. Nitobi <ul><li>Nitobi co-founder, CTO </li></ul><ul><li>Located in Vancouver, Canada </li></ul><ul><li>Declarative, server integrated, Ajax user-interface components </li></ul><ul><li>User interface consulting and training services </li></ul><ul><li>Online usability service RobotReplay </li></ul>
    5. 6. Customers
    6. 7. Patterns OVERVIEW
    7. 8. <ul><li>“Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice” </li></ul><ul><li>A Pattern Language </li></ul>
    8. 9. Architectural Patterns <ul><li>Literally. </li></ul><ul><li>MVC </li></ul><ul><li>Peer-to-peer </li></ul><ul><li>SOA </li></ul>
    9. 10. Software Design Patterns <ul><li>Gang of Four (GoF) </li></ul><ul><ul><li>Creational </li></ul></ul><ul><ul><li>Behavioural </li></ul></ul><ul><ul><li>Structural </li></ul></ul><ul><li>Others </li></ul><ul><ul><li>Concurrency </li></ul></ul>
    10. 11. UI Design Patterns <ul><li>Bill Scott and others </li></ul><ul><li>Described as </li></ul><ul><ul><li>Problem </li></ul></ul><ul><ul><li>Context </li></ul></ul><ul><ul><li>Principle </li></ul></ul><ul><ul><li>Solution </li></ul></ul><ul><ul><li>Why / How </li></ul></ul>
    11. 12. Ajax Design Patterns <ul><li>What we are talking about today </li></ul><ul><li>Applies principles from all areas: </li></ul><ul><ul><li>MVC </li></ul></ul><ul><ul><li>UI </li></ul></ul><ul><ul><li>OOP </li></ul></ul>
    12. 13. Bad Patterns <ul><li>Increased complexity </li></ul><ul><li>Does not provide re-use </li></ul><ul><li>Language hacks </li></ul>THE PROBLEMS
    13. 14. Observer <ul><li>Event = function(type) { </li></ul><ul><li>this.handlers = {}; </li></ul><ul><li>this.guid = 0; </li></ul><ul><li>} </li></ul><ul><li>Event.prototype.subscribe = function(func) { </li></ul><ul><li>this.handlers[this.guid++] = func; </li></ul><ul><li>return guid; </li></ul><ul><li>} </li></ul><ul><li>Event.prototype.notify = function(evtArgs) { </li></ul><ul><li>for (var item in this.handlers) { </li></ul><ul><li>this.handlers[item].apply(this, arguments); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    14. 15. <ul><li>“The meaning of life is that it is to be lived, and it is not to be traded and conceptualized and squeezed into a pattern of systems” </li></ul><ul><li>Bruce Lee </li></ul>
    15. 16. Programmers Are Lazy <ul><li>“ If you have a difficult task, give it to a lazy person - they will find an easier way to do it.” </li></ul><ul><li>Hlade's Law </li></ul>
    16. 17. Ajax Patterns - Goals <ul><li>Making the web like the desktop </li></ul><ul><li>Improving user experience </li></ul><ul><li>More responsive applications </li></ul><ul><li>Remove web / desktop divide </li></ul><ul><li>Marriage of JavaScript, DOM, CSS </li></ul>
    17. 18. Describing Ajax Patterns <ul><li>What is the pattern </li></ul><ul><li>When is the pattern applied </li></ul><ul><li>Why use the pattern </li></ul><ul><li>How is the pattern implemented </li></ul>
    18. 19. Best Practices <ul><li>Consider the user and the developer </li></ul><ul><li>Common approaches </li></ul><ul><ul><li>Progressive enhancement </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul><ul><ul><li>Unobtrusive JavaScript </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
    19. 20. Pitfalls <ul><li>Cross browser </li></ul><ul><li>Cross doctype </li></ul><ul><li>I18n and localization </li></ul><ul><li>Accessibility </li></ul>
    20. 21. THE PATTERNS
    21. 22. Inline Edit <ul><li>What edit in page without page refresh </li></ul><ul><li>When titles, tags etc </li></ul><ul><li>Why remove page refreshes </li></ul>
    22. 23. How <ul><li>User invited to click on data field </li></ul><ul><li>Field is replaced with an editing control </li></ul><ul><li>User blurs or saves and field is saved behind the scenes and editor removed </li></ul>
    23. 25. DOM <ul><li><div id=&quot;title&quot; class=&quot;editable&quot;>Edit me!</div> </li></ul>
    24. 26. CSS <ul><li>#title { </li></ul><ul><li>width:200px; </li></ul><ul><li>border:1px solid black; </li></ul><ul><li>cursor:pointer; </li></ul><ul><li>cursor:hand; </li></ul><ul><li>} </li></ul>
    25. 27. Edit <ul><li>edit: function(evt) { </li></ul><ul><li>//create the editor </li></ul><ul><li>this.editor = nitobi.html.createElement( “input”, { id : ”editor”, type : ”text”, value : this.node.innerHTML }, { width: this.node.offsetWidth, height: this.node.offsetHeight ); </li></ul><ul><li>this.node.replaceChild(this.editor, this.node.firstChild); </li></ul><ul><li>//set the focus so that it is ready to go </li></ul><ul><li>this.editor.focus(); </li></ul><ul><li>//attach events for bluring and key handling </li></ul><ul><li>nitobi.html.attachEvent(this.editor, &quot;blur&quot;,; </li></ul><ul><li>nitobi.html.attachEvent(this.editor, &quot;keydown&quot;, this.keydown); </li></ul><ul><li>//detach the event for going into edit mode </li></ul><ul><li>nitobi.html.detachEvent(this.node, &quot;click&quot;, this.edit); </li></ul><ul><li>} </li></ul>
    26. 28. Key Handling <ul><li>keydown: function(evt) { </li></ul><ul><li>//check for enter key at least </li></ul><ul><li>if (evt.keyCode == 13) { </li></ul><ul><li>this.editor.blur(); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    27. 29. Saving <ul><li>save: function(evt) { </li></ul><ul><li>//send the data to the server </li></ul><ul><li>var xhr = new HttpRequest(); </li></ul><ul><li>xhr.onRequestComplete.subscribe(this.afterSave); </li></ul><ul><li>“POST”, “”, true); </li></ul><ul><li>xhr.send(“title=“+this.editor.value); </li></ul><ul><li>//revert to the view only </li></ul><ul><li>this.node.innerHTML = this.editor.value; </li></ul><ul><li>//show an activity indicator </li></ul><ul><li> = “inline”; </li></ul><ul><li>}, </li></ul><ul><li>afterSave: function(httpResponse) { </li></ul><ul><li>//concurrency pattern </li></ul><ul><li>//undo with command pattern </li></ul><ul><li>attachEvent(this.node, &quot;click&quot;, this.edit); </li></ul><ul><li>} </li></ul>
    28. 30. Pitfalls / Best Practices <ul><li>Potential Pitfalls </li></ul><ul><ul><li>page contents can move when changing “modes” </li></ul></ul><ul><ul><li>too subtle invitation to edit </li></ul></ul><ul><ul><li>too many edit invitations </li></ul></ul><ul><ul><li>concurrency or failure </li></ul></ul><ul><li>Best Practices </li></ul><ul><ul><li>avoid page jitter </li></ul></ul><ul><ul><li>make render & edit modes same size </li></ul></ul><ul><ul><li>activate on click </li></ul></ul><ul><ul><li>deactivate on blur </li></ul></ul>
    29. 31. Composite Controls <ul><li>What inline edit multiple text fields </li></ul><ul><li>When simple forms, combo box </li></ul><ul><li>Why remove page refreshes </li></ul>
    30. 32. How <ul><li>Create a Form JavaScript class that implements IBlurable interface </li></ul><ul><li>All form elements have key and mouse events attached through IBlurable </li></ul><ul><li>When user clicks on fields the mousedown event on the newly clicked field blocks the blur on the previous field </li></ul><ul><li>When user clicks outside of the composite control the blur event is not blocked </li></ul>
    31. 33. Aside: Event Order <ul><li>Click on the first name field </li></ul><ul><ul><li>mousedown </li></ul></ul><ul><ul><li>focus </li></ul></ul><ul><ul><li>mouseup </li></ul></ul><ul><ul><li>click </li></ul></ul><ul><li>Click on the last name field </li></ul><ul><ul><li>mousedown (last name) </li></ul></ul><ul><ul><li>blur (first name) </li></ul></ul><ul><ul><li>focus (last name) </li></ul></ul><ul><ul><li>mouseup (last name) </li></ul></ul><ul><ul><li>click (last name) </li></ul></ul><ul><li>Also consider relatedTarget / fromElement </li></ul>
    32. 35. DOM <ul><li><form id=&quot;form&quot; name=&quot;form&quot;> </li></ul><ul><li><label for=&quot;first&quot;>First: </label> </li></ul><ul><li><input name=&quot;first&quot; id=&quot;first&quot;> </li></ul><ul><li><label for=&quot;last&quot;>Last: </label> </li></ul><ul><li><input name=&quot;last&quot; id=&quot;last&quot;> </li></ul><ul><li></form> </li></ul>
    33. 36. NameForm <ul><li>NameForm = function(node) { </li></ul><ul><li>this.form = node; </li></ul><ul><li>//setup the blurable interface </li></ul><ul><li>, this.form.elements); </li></ul><ul><li>this.onBlur.subscribe(, this); </li></ul><ul><li>} </li></ul><ul><li>//implement the interface (ie copy over the methods) </li></ul><ul><li>nitobi.lang.implement(NameForm, IBlurable); </li></ul><ul><li> = function(evt) { </li></ul><ul><li>//same issues as inline edit case </li></ul><ul><li>var xhr = new nitobi.ajax.HttpRequest(); </li></ul><ul><li>;POST&quot;, &quot;;, true); </li></ul><ul><li>xhr.send(this.form); </li></ul><ul><li>}; </li></ul>
    34. 37. Pitfalls / Best Practices <ul><li>Potential Pitfalls </li></ul><ul><ul><li>event order differences across browsers </li></ul></ul><ul><ul><li>event problems on certain controls (select, scrollbars) </li></ul></ul><ul><li>Best Practices </li></ul><ul><ul><li>use for data validation behind the scenes on sub-forms </li></ul></ul><ul><ul><li>consider both keyboard (tabbing) and mouse events </li></ul></ul>
    35. 38. Popup <ul><li>What display additional information </li></ul><ul><li>When details are required in context </li></ul><ul><li>Why remove page refresh </li></ul>
    36. 39. How <ul><li>User moves mouse over element that has mouseover event attached </li></ul><ul><li>Popup is displayed </li></ul><ul><li>User moves over another part of the trigger element firing mouseout event on a timeout </li></ul><ul><li>If show is called while waiting for a hide the hide is cancelled preventing flicker and allowing user to mouse over the contents of the popup </li></ul>
    37. 41. DOM <ul><li><span id=&quot;title&quot;> </li></ul><ul><li><strong> </li></ul><ul><li><a href=&quot;#&quot;>Iron Man</a> </li></ul><ul><li></strong> </li></ul><ul><li><span class=&quot;year&quot;>(2008)</span> </li></ul><ul><li></span> </li></ul><ul><li><div id=&quot;details&quot;> </li></ul><ul><li>When wealthy ... </li></ul><ul><li><a href=&quot;#&quot;>(more)</a> </li></ul><ul><li></div> </li></ul>
    38. 42. Aside: Event Bubbling <ul><li>Events bubble up through the DOM </li></ul><ul><li>They are also “captured” </li></ul><ul><li>Event handlers fire when trigged from any child node </li></ul>
    39. 43. CSS <ul><li>#details { </li></ul><ul><li>position: absolute; </li></ul><ul><li>display: none; </li></ul><ul><li>top: 0px; </li></ul><ul><li>left: 0px; </li></ul><ul><li>width: 300px; </li></ul><ul><li>height: 100px; </li></ul><ul><li>border: 1px solid black; </li></ul><ul><li>background-color: white; </li></ul><ul><li>} </li></ul>
    40. 44. Constructor <ul><li>Popup = function(title, detail) { </li></ul><ul><li>this.detail = detail; </li></ul><ul><li>//attach mouseover event for show </li></ul><ul><li>nitobi.html.attachEvent(title, &quot;mouseover&quot;,; </li></ul><ul><li>var _t = this; </li></ul><ul><li>this.hideTimeout = null; </li></ul><ul><li>//attach a delayed mouseout event for hide </li></ul><ul><li>nitobi.html.attachEvent(title, &quot;mouseout&quot;, function() { </li></ul><ul><li>_t.hideTimeout = setTimeout(_t.hide, 200); </li></ul><ul><li>}); </li></ul><ul><li>} </li></ul>
    41. 45. Show <ul><li> = function(evt) { </li></ul><ul><li>if (this.hideTimeout != null) { </li></ul><ul><li>//clear the hide timeout clearTimeout(this.hideTimeout); </li></ul><ul><li>this.hideTimeout </li></ul><ul><li>} else { </li></ul><ul><li>//show the popup </li></ul><ul><li>var style =; </li></ul><ul><li>style.display = &quot;block&quot;; </li></ul><ul><li> = (evt.clientY + 5) + &quot;px&quot;; </li></ul><ul><li>style.left = (evt.clientX + 5) + &quot;px&quot;; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    42. 46. Pitfalls / Best Practices <ul><li>Potential Pitfalls </li></ul><ul><ul><li>mouse event bubbling causes flickering or moving of the popup </li></ul></ul><ul><ul><li>rogue popups </li></ul></ul><ul><ul><li>Interaction between trigger and contents </li></ul></ul><ul><li>Best Practices </li></ul><ul><ul><li>enable keyboard access </li></ul></ul><ul><ul><li>provide some way to close manually </li></ul></ul><ul><ul><li>make certain it disappears! </li></ul></ul>
    43. 47. Copy and Paste <ul><li>What user can enter bulk data </li></ul><ul><li>When interop desktop and browser </li></ul><ul><li>Why remove page refreshes </li></ul>
    44. 48. How - Copy <ul><li>User clicks on focusable element (<a>) </li></ul><ul><li>Filter for ctrl+c on keydown event </li></ul><ul><li>Set value of hidden <textarea> to the data that is to be copied </li></ul><ul><li>Focus on the hidden <textarea> </li></ul><ul><li>Magic </li></ul><ul><li>Capture keyup event on hidden <textarea> to focus back on your control </li></ul><ul><li><textarea> value now on OS clipboard </li></ul>
    45. 49. How - Paste <ul><li>User clicks on focusable element (<a>) </li></ul><ul><li>Filter for ctrl+v on keydown event </li></ul><ul><li>Focus on the hidden <textarea> </li></ul><ul><li>Magic </li></ul><ul><li>Capture keyup event on hidden <textarea> </li></ul><ul><li>Data from OS clipboard now in the <textarea> where you can access it and focus back on your control </li></ul>
    46. 51. DOM <ul><li><div id=&quot;copyable&quot;> </li></ul><ul><li>Copy this text </li></ul><ul><li></div> </li></ul><ul><li><textarea id=“clipboard”></textarea> </li></ul>
    47. 52. CSS <ul><li>#clipboard { </li></ul><ul><ul><li>position: absolute; </li></ul></ul><ul><ul><li>left: -5000px; </li></ul></ul><ul><li>} </li></ul>
    48. 53. Constructor <ul><li>var Copyable = function(node) { </li></ul><ul><li>//replace the contents with an <a> tag so that it is focusable </li></ul><ul><li>this.source = node; </li></ul><ul><li>this.source.innerHTML = &quot;<a href=&quot;#&quot; class=&quot;focusable&quot;>&quot;+this.source.innerHTML+&quot;</a>&quot;; </li></ul><ul><li>//intercept all key presses to look for ctrl+c/v </li></ul><ul><li>nitobi.html.attachEvent(this.source, &quot;keydown&quot;, this.handleKey); </li></ul><ul><li>//create the clipboard </li></ul><ul><li>this.clipboard = nitobi.html.createElement(&quot;textarea&quot;, { id : &quot;clipboard&quot; } ); </li></ul><ul><li>document.body.appendChild(this.clipboard); </li></ul><ul><li>} </li></ul>
    49. 54. handleKey <ul><li>handleKey: function(evt) { </li></ul><ul><li>var k = evt.keyCode; </li></ul><ul><li>//offset keycode for modifier keys </li></ul><ul><li>k = k + (evt.shiftKey?256:0)+ </li></ul><ul><li>(evt.ctrlKey?512:0)+ </li></ul><ul><li>(evt.metaKey?1024:0); </li></ul><ul><li>//lookup the method in a map </li></ul><ul><li>var handler = this.keyMap[k]; </li></ul><ul><li>//call that method </li></ul><ul><li>if (handler != null) </li></ul><ul><li>; </li></ul><ul><li>} </li></ul>
    50. 55. Copy <ul><li>copy: function() { </li></ul><ul><li>//get the data we want to copy </li></ul><ul><li>var data = this.source.firstChild.innerHTML; </li></ul><ul><li>if (!nitobi.browser.IE) { </li></ul><ul><li>//focus back control when the copy is complete </li></ul><ul><li>attachEvent(this.clipboard, &quot;keyup&quot;, this.focus); </li></ul><ul><li>//set the value, focus and select the value </li></ul><ul><li>this.clipboard.value = data; </li></ul><ul><li>this.clipboard.focus(); this.clipboard.setSelectionRange(0,data.length); </li></ul><ul><li>} else { </li></ul><ul><li>window.clipboardData.setData(&quot;Text&quot;,data); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    51. 56. Paste <ul><li>paste: function() { </li></ul><ul><li>//catch the textarea keyup to grab the results </li></ul><ul><li>nitobi.html.attachEventOnce(this.clipboard, &quot;keyup&quot;, this.pasteReady); </li></ul><ul><li>this.clipboard.focus(); </li></ul><ul><li>}, </li></ul><ul><li>pasteReady: function() { </li></ul><ul><li>//get the clipboard value and insert it </li></ul><ul><li>this.source.firstChild.innerHTML = this.clipboard.value; </li></ul><ul><li>//focus back on the control </li></ul><ul><li>this.source.firstChild.focus(); </li></ul><ul><li>} </li></ul>
    52. 57. Pitfalls / Best Practices <ul><li>Pitfalls </li></ul><ul><ul><li>user is unaware of functionality </li></ul></ul><ul><ul><li>keyboard short cuts don’t match OS </li></ul></ul><ul><ul><li>no context menu support in some browsers </li></ul></ul><ul><li>Best Practices </li></ul><ul><ul><li>capture ctrl/cmd+c/v/x for different OS </li></ul></ul><ul><ul><li>support various formats depending on use case </li></ul></ul>
    53. 58. Live Scrolling <ul><li>What scroll through large datasets </li></ul><ul><li>When viewing, filtering, sorting </li></ul><ul><li>Why remove page refreshes </li></ul>
    54. 59. How <ul><li>Create clipping <div> and surface <div> where data goes </li></ul><ul><li>Repeat for a scrollbar </li></ul><ul><li>Connect scrollbar scroll events to position the data surface </li></ul><ul><li>Retrieve data from the server as user scrolls </li></ul>
    55. 61. DOM <ul><li><div id=“viewport”> </li></ul><ul><li><div id=“surface”> </li></ul><ul><li><div id=“data”></div> </li></ul><ul><li></div> </li></ul><ul><li></div> </li></ul><ul><li><div id=“scrollcontainer&quot;> </li></ul><ul><li><div id=&quot;scrollbar&quot;> </li></ul><ul><li><div id=“range”></div> </li></ul><ul><li></div> </li></ul><ul><li></div> </li></ul>
    56. 62. CSS <ul><li>#scrollcontainer { </li></ul><ul><li>width: 16px; </li></ul><ul><li>height: 300px; </li></ul><ul><li>overflow: hidden; </li></ul><ul><li>} </li></ul><ul><li>#scrollbar { </li></ul><ul><li>height: 100%; </li></ul><ul><li>width: 16px; </li></ul><ul><li>//width: 17px; </li></ul><ul><li>position: relative; </li></ul><ul><li>overflow-x: hidden; </li></ul><ul><li>overflow-y: scroll; </li></ul><ul><li>} </li></ul><ul><li>#range { </li></ul><ul><li>overflow: hidden; </li></ul><ul><li>width: 1px; </li></ul><ul><li>height: 3000px;&quot; </li></ul><ul><li>} </li></ul>#viewport { position: relative; overflow: hidden; width: 300px; height: 300px; border: 1px solid black; } #surface { width: 2000px; height: 2000px; }
    57. 63. Scrolling <ul><li>var evt = “mousewheel”; </li></ul><ul><li>if (nitobi.browser.MOZ) </li></ul><ul><li>evt = “DOMMouseScroll”; </li></ul><ul><li>//attach the event to the surface </li></ul><ul><li>nitobi.html.attachEvent( </li></ul><ul><li>scollSurface, evt, </li></ul><ul><li>this.handleMouseWheel); </li></ul>
    58. 64. Pitfalls / Best Practices <ul><li>Potential Pitfalls </li></ul><ul><ul><li>dual-scrollbar issue </li></ul></ul><ul><ul><li>sluggish performance </li></ul></ul><ul><ul><li>extremely large data sets </li></ul></ul><ul><li>Best Practices </li></ul><ul><ul><li>provide dynamic tooltip showing location within scroll </li></ul></ul><ul><ul><li>animate scroll </li></ul></ul><ul><ul><li>if desire a hybrid, use animation on paging. </li></ul></ul><ul><ul><li>support mouse scroll wheel </li></ul></ul>
    59. 65. Acknowledgements <ul><li>Bill Scott </li></ul><ul><li>Michael Mahemoff </li></ul><ul><li>Andre Charland </li></ul>
    60. 66. Thank You <ul><li>Questions? </li></ul><ul><li>email: [email_address] </li></ul><ul><li>blog: </li></ul><ul><li>book: </li></ul><ul><li>tweets: </li></ul>
    61. 67. <ul><li> </li></ul><ul><li> </li></ul><ul><li> </li></ul>