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

JavaScript and DOM Pattern Implementation

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