Slideshow transcript
Slide 1: JavaScript DOM Pattern Implementations Dave Johnson dave@nitobi.com CommunityOne :: May 5 :: 2008
Slide 2: Agenda • About me • Patterns Overview • Patterns – Inline Editing – Composite Controls – Popup – Copy and Paste – Live Scrolling CommunityOne :: May 5 :: 2008
Slide 3: About • Author Enterprise Ajax • http://blogs.nitobi.com/dave CommunityOne :: May 5 :: 2008
Slide 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 CommunityOne :: May 5 :: 2008
Slide 5: CommunityOne :: May 5 :: 2008
Slide 6: Customers CommunityOne :: May 5 :: 2008
Slide 7: Patterns OVERVIEW CommunityOne :: May 5 :: 2008
Slide 8: “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” CommunityOne :: May 5 :: 2008
Slide 9: Architectural Patterns Literally. • MVC • Peer-to-peer • SOA CommunityOne :: May 5 :: 2008
Slide 10: Software Design Patterns • Gang of Four (GoF) – Creational – Behavioural – Structural • Others – Concurrency CommunityOne :: May 5 :: 2008
Slide 11: UI Design Patterns • Bill Scott and others • Described as – Problem – Context – Principle – Solution – Why / How CommunityOne :: May 5 :: 2008
Slide 12: Ajax Design Patterns • What we are talking about today • Applies principles from all areas: – MVC – UI – OOP CommunityOne :: May 5 :: 2008
Slide 13: Bad Patterns • Increased complexity • Does not provide re-use • Language hacks THE PROBLEMS CommunityOne :: May 5 :: 2008
Slide 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); } } CommunityOne :: May 5 :: 2008
Slide 15: “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 CommunityOne :: May 5 :: 2008
Slide 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 CommunityOne :: May 5 :: 2008
Slide 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 CommunityOne :: May 5 :: 2008
Slide 18: Describing Ajax Patterns What is the pattern When is the pattern applied Why use the pattern How is the pattern implemented CommunityOne :: May 5 :: 2008
Slide 19: 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 CommunityOne :: May 5 :: 2008
Slide 20: Pitfalls • Cross browser • Cross doctype • I18n and localization • Accessibility CommunityOne :: May 5 :: 2008
Slide 21: THE PATTERNS CommunityOne :: May 5 :: 2008
Slide 22: Inline Edit • What edit in page without page refresh • When titles, tags etc • Why remove page refreshes CommunityOne :: May 5 :: 2008
Slide 23: How 1. User invited to click on data field 2. Field is replaced with an editing control 3. User blurs or saves and field is saved behind the scenes and editor removed CommunityOne :: May 5 :: 2008
Slide 24: CommunityOne :: May 5 :: 2008
Slide 25: DOM <div id="title" class="editable">Edit me!</div> CommunityOne :: May 5 :: 2008
Slide 26: CSS #title { width:200px; border:1px solid black; cursor:pointer; cursor:hand; } CommunityOne :: May 5 :: 2008
Slide 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, "blur", this.save); nitobi.html.attachEvent(this.editor, "keydown", this.keydown); //detach the event for going into edit mode nitobi.html.detachEvent(this.node, "click", this.edit); } CommunityOne :: May 5 :: 2008
Slide 28: Key Handling keydown: function(evt) { //check for enter key at least if (evt.keyCode == 13) { this.editor.blur(); } } CommunityOne :: May 5 :: 2008
Slide 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, "click", this.edit); } CommunityOne :: May 5 :: 2008
Slide 30: 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 CommunityOne :: May 5 :: 2008
Slide 31: Composite Controls • What inline edit multiple text fields • When simple forms, combo box • Why remove page refreshes CommunityOne :: May 5 :: 2008
Slide 32: How 1. Create a Form JavaScript class that implements IBlurable interface 2. All form elements have key and mouse events attached through IBlurable 3. When user clicks on fields the mousedown event on the newly clicked field blocks the blur on the previous field 4. When user clicks outside of the composite control the blur event is not blocked :: 2008 CommunityOne :: May 5
Slide 33: 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 5 :: 2008 CommunityOne :: May
Slide 34: CommunityOne :: May 5 :: 2008
Slide 35: DOM <form id="form" name="form"> <label for="first">First: </label> <input name="first" id="first"> <label for="last">Last: </label> <input name="last" id="last"> </form> CommunityOne :: May 5 :: 2008
Slide 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("POST", "http://nitobi.com/name", true); xhr.send(this.form); }; CommunityOne :: May 5 :: 2008
Slide 37: 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 CommunityOne :: May 5 :: 2008
Slide 38: Popup • What display additional information • When details are required in context • Why remove page refresh CommunityOne :: May 5 :: 2008
Slide 39: How 1. User moves mouse over element that has mouseover event attached 2. Popup is displayed 3. User moves over another part of the trigger element firing mouseout event on a timeout 4. 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 CommunityOne :: May 5 :: 2008
Slide 40: CommunityOne :: May 5 :: 2008
Slide 41: DOM <span id="title"> <strong> <a href="#">Iron Man</a> </strong> <span class="year">(2008)</span> </span> <div id="details"> When wealthy ... <a href="#">(more)</a> </div> CommunityOne :: May 5 :: 2008
Slide 42: Aside: Event Bubbling • Events bubble up through the DOM • They are also “captured” • Event handlers fire when trigged from any child node CommunityOne :: May 5 :: 2008
Slide 43: CSS #details { position: absolute; display: none; top: 0px; left: 0px; width: 300px; height: 100px; border: 1px solid black; background-color: white; } CommunityOne :: May 5 :: 2008
Slide 44: Constructor Popup = function(title, detail) { this.detail = detail; //attach mouseover event for show nitobi.html.attachEvent(title, "mouseover", this.show); var _t = this; this.hideTimeout = null; //attach a delayed mouseout event for hide nitobi.html.attachEvent(title, "mouseout", function() { _t.hideTimeout = setTimeout(_t.hide, 200); }); } CommunityOne :: May 5 :: 2008
Slide 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 = "block"; style.top = (evt.clientY + 5) + "px"; style.left = (evt.clientX + 5) + "px"; } } CommunityOne :: May 5 :: 2008
Slide 46: 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! CommunityOne :: May 5 :: 2008
Slide 47: Copy and Paste • What user can enter bulk data • When interop desktop and browser • Why remove page refreshes CommunityOne :: May 5 :: 2008
Slide 48: How - Copy 1. User clicks on focusable element (<a>) 2. Filter for ctrl+c on keydown event 3. Set value of hidden <textarea> to the data that is to be copied 4. Focus on the hidden <textarea> 5. Magic 6. Capture keyup event on hidden <textarea> to focus back on your control CommunityOne :: May 5 :: 2008
Slide 49: How - Paste 1. User clicks on focusable element (<a>) 2. Filter for ctrl+v on keydown event 3. Focus on the hidden <textarea> 4. Magic 5. Capture keyup event on hidden <textarea> 6. Data from OS clipboard now in the <textarea> where you can access it and focus back on your control CommunityOne :: May 5 :: 2008
Slide 50: CommunityOne :: May 5 :: 2008
Slide 51: DOM <div id="copyable"> Copy this text </div> <textarea id=“clipboard”></textarea> CommunityOne :: May 5 :: 2008
Slide 52: CSS #clipboard { position: absolute; left: -5000px; } CommunityOne :: May 5 :: 2008
Slide 53: Constructor var Copyable = function(node) { //replace the contents with an <a> tag so that it is focusable this.source = node; this.source.innerHTML = "<a href="#" class="focusable">"+this.source.innerHTML+"</a>"; //intercept all key presses to look for ctrl+c/v nitobi.html.attachEvent(this.source, "keydown", this.handleKey); //create the clipboard this.clipboard = nitobi.html.createElement("textarea", { id : "clipboard"+node.id } ); document.body.appendChild(this.clipboard); } CommunityOne :: May 5 :: 2008
Slide 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); } CommunityOne :: May 5 :: 2008
Slide 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, "keyup", 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("Text",data); } } CommunityOne :: May 5 :: 2008
Slide 56: Paste paste: function() { //catch the textarea keyup to grab the results nitobi.html.attachEventOnce(this.clipboard, "keyup", 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(); } CommunityOne :: May 5 :: 2008
Slide 57: 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 CommunityOne :: May 5 :: 2008
Slide 58: Live Scrolling • What scroll through large datasets • When viewing, filtering, sorting • Why remove page refreshes CommunityOne :: May 5 :: 2008
Slide 59: How 1. Create clipping <div> and surface <div> where data goes 2. Repeat for a scrollbar 3. Connect scrollbar scroll events to position the data surface 4. Retrieve data from the server as user scrolls CommunityOne :: May 5 :: 2008
Slide 60: CommunityOne :: May 5 :: 2008
Slide 61: DOM <div id=“viewport”> <div id=“surface”> <div id=“data”></div> </div> </div> <div id=“scrollcontainer"> <div id="scrollbar"> <div id=“range”></div> </div> </div> CommunityOne :: May 5 :: 2008
Slide 62: CSS #viewport { #scrollcontainer { position: relative; width: 16px; overflow: hidden; height: 300px; width: 300px; overflow: hidden; height: 300px; } border: 1px solid black; #scrollbar { } height: 100%; width: 16px; #surface { //width: 17px; width: 2000px; position: relative; height: 2000px; overflow-x: hidden; } overflow-y: scroll; } #range { overflow: hidden; width: 1px; height: 3000px;" } CommunityOne :: May 5 :: 2008
Slide 63: Scrolling var evt = “mousewheel”; if (nitobi.browser.MOZ) evt = “DOMMouseScroll”; //attach the event to the surface nitobi.html.attachEvent( scollSurface, evt, this.handleMouseWheel); CommunityOne :: May 5 :: 2008
Slide 64: 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. CommunityOne :: May 5 :: 2008
Slide 65: Acknowledgements • Bill Scott • Michael Mahemoff • Andre Charland CommunityOne :: May 5 :: 2008
Slide 66: Thank You Questions? email: dave@nitobi.com blog: blogs.nitobi.com/dave book: enterpriseajax.com tweets: twitter.com/davejohnson CommunityOne :: May 5 :: 2008
Slide 67: http://flickr.com/photos/daveknapik/2115474105/ http://flickr.com/photos/preciouskhyatt/2153351428/ http://flickr.com/photos/sandcastlematt/403101240/ CommunityOne :: May 5 :: 2008



Add a comment on Slide 1
If you have a SlideShare account, login to comment; else you can comment as a guest- Favorites & Groups
Showing 1-50 of 4 (more)