JavaScript and DOM Pattern Implementation

  • View
    6.195

  • Download
    4

  • Category

    Business

Preview:

DESCRIPTION

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

Citation preview

CommunityOne :: May 5 :: 2008

JavaScript DOM PatternImplementations

Dave Johnsondave@nitobi.com

CommunityOne :: May 5 :: 2008

Agenda

• About me• Patterns Overview• Patterns

– Inline Editing– Composite Controls– Popup– Copy and Paste– Live Scrolling

CommunityOne :: May 5 :: 2008

About

• Author Enterprise Ajax• http://blogs.nitobi.com/dave

CommunityOne :: May 5 :: 2008

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

CommunityOne :: May 5 :: 2008

Customers

CommunityOne :: May 5 :: 2008

Patterns

OVERVIEW

CommunityOne :: May 5 :: 2008

“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

CommunityOne :: May 5 :: 2008

Architectural Patterns

Literally.

• MVC• Peer-to-peer• SOA

CommunityOne :: May 5 :: 2008

Software Design Patterns

• Gang of Four (GoF)– Creational– Behavioural– Structural

• Others– Concurrency

CommunityOne :: May 5 :: 2008

UI Design Patterns

• Bill Scott and others• Described as

– Problem– Context– Principle– Solution– Why / How

CommunityOne :: May 5 :: 2008

Ajax Design Patterns

• What we are talking about today

• Applies principles from all areas:– MVC– UI– OOP

CommunityOne :: May 5 :: 2008

Bad Patterns

• Increased complexity• Does not provide re-use• Language hacks

THE PROBLEMS

CommunityOne :: May 5 :: 2008

ObserverEvent = 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

“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

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

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

Describing Ajax Patterns

What is the patternWhen is the pattern appliedWhy use the patternHow is the pattern implemented

CommunityOne :: May 5 :: 2008

Best Practices

• Consider the user and the developer

• Common approaches– Progressive enhancement

http://en.wikipedia.org/wiki/Progressive_enhancement

– Unobtrusive JavaScripthttp://en.wikipedia.org/wiki/Unobtrusive_JavaScript

CommunityOne :: May 5 :: 2008

Pitfalls

• Cross browser• Cross doctype• I18n and localization• Accessibility

CommunityOne :: May 5 :: 2008

THE PATTERNS

CommunityOne :: May 5 :: 2008

Inline Edit

• What edit in page without page refresh

• When titles, tags etc• Why remove page refreshes

CommunityOne :: May 5 :: 2008

How

1. User invited to click on data field2. Field is replaced with an editing

control3. User blurs or saves and field is

saved behind the scenes and editor removed

CommunityOne :: May 5 :: 2008

CommunityOne :: May 5 :: 2008

DOM

<div id="title" class="editable">Edit me!</div>

CommunityOne :: May 5 :: 2008

CSS

#title {

width:200px;

border:1px solid black;

cursor:pointer;

cursor:hand;

}

CommunityOne :: May 5 :: 2008

Editedit: function(evt) {

//create the editorthis.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 gothis.editor.focus();

//attach events for bluring and key handlingnitobi.html.attachEvent(this.editor, "blur", this.save);nitobi.html.attachEvent(this.editor, "keydown", this.keydown);

//detach the event for going into edit modenitobi.html.detachEvent(this.node, "click", this.edit);

}

CommunityOne :: May 5 :: 2008

Key Handling

keydown: function(evt) {

//check for enter key at least

if (evt.keyCode == 13) {

this.editor.blur();

}

}

CommunityOne :: May 5 :: 2008

Savingsave: function(evt) {

//send the data to the servervar xhr = new HttpRequest();xhr.onRequestComplete.subscribe(this.afterSave);xhr.open(“POST”, “mysite.com”, true);xhr.send(“title=“+this.editor.value);//revert to the view onlythis.node.innerHTML = this.editor.value;//show an activity indicatorthis.activity.style.display = “inline”;

},afterSave: function(httpResponse) {

//concurrency pattern//undo with command patternattachEvent(this.node, "click", this.edit);

}

CommunityOne :: May 5 :: 2008

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

Composite Controls

• What inline edit multiple text fields• When simple forms, combo box• Why remove page refreshes

CommunityOne :: May 5 :: 2008

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

CommunityOne :: May 5 :: 2008

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

CommunityOne :: May 5 :: 2008

CommunityOne :: May 5 :: 2008

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

NameFormNameForm = function(node) {

this.form = node;//setup the blurable interfaceIBlurable.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 casevar xhr = new nitobi.ajax.HttpRequest();xhr.open("POST", "http://nitobi.com/name", true);xhr.send(this.form);

};

CommunityOne :: May 5 :: 2008

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

CommunityOne :: May 5 :: 2008

Popup

• What display additional information• When details are required in context• Why remove page refresh

CommunityOne :: May 5 :: 2008

How

1. User moves mouse over element that has mouseover event attached

2. Popup is displayed3. 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

CommunityOne :: May 5 :: 2008

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

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

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

ConstructorPopup = function(title, detail) {

this.detail = detail;//attach mouseover event for shownitobi.html.attachEvent(title, "mouseover", this.show);var _t = this;this.hideTimeout = null;//attach a delayed mouseout event for hidenitobi.html.attachEvent(title, "mouseout", function() {

_t.hideTimeout = setTimeout(_t.hide, 200);});

}

CommunityOne :: May 5 :: 2008

ShowPopup.prototype.show = function(evt) {

if (this.hideTimeout != null) {//clear the hide timeout

clearTimeout(this.hideTimeout);this.hideTimeout

} else {//show the popupvar style = this.detail.style;style.display = "block";style.top = (evt.clientY + 5) + "px";style.left = (evt.clientX + 5) + "px";

}}

CommunityOne :: May 5 :: 2008

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

Copy and Paste

• What user can enter bulk data• When interop desktop and browser• Why remove page refreshes

CommunityOne :: May 5 :: 2008

How - Copy

1. User clicks on focusable element (<a>)2. Filter for ctrl+c on keydown event3. Set value of hidden <textarea> to the

data that is to be copied4. Focus on the hidden <textarea>5. Magic6. Capture keyup event on hidden

<textarea> to focus back on your control7. <textarea> value now on OS clipboard

CommunityOne :: May 5 :: 2008

How - Paste

1. User clicks on focusable element (<a>)2. Filter for ctrl+v on keydown event3. Focus on the hidden <textarea>4. Magic5. 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

CommunityOne :: May 5 :: 2008

DOM

<div id="copyable">

Copy this text

</div>

<textarea id=“clipboard”></textarea>

CommunityOne :: May 5 :: 2008

CSS

#clipboard {position: absolute;

left: -5000px;

}

CommunityOne :: May 5 :: 2008

Constructorvar Copyable = function(node) {

//replace the contents with an <a> tag so that it is focusablethis.source = node;this.source.innerHTML = "<a href=\"#\" class=\"focusable\">"+this.source.innerHTML+"</a>";

//intercept all key presses to look for ctrl+c/vnitobi.html.attachEvent(this.source, "keydown", this.handleKey);

//create the clipboardthis.clipboard = nitobi.html.createElement("textarea", { id : "clipboard"+node.id } );document.body.appendChild(this.clipboard);

}

CommunityOne :: May 5 :: 2008

handleKey

handleKey: function(evt) {var k = evt.keyCode;//offset keycode for modifier keysk = k + (evt.shiftKey?256:0)+

(evt.ctrlKey?512:0)+(evt.metaKey?1024:0);

//lookup the method in a mapvar handler = this.keyMap[k];//call that methodif (handler != null)

handler.call(this);}

CommunityOne :: May 5 :: 2008

Copycopy: function() {

//get the data we want to copyvar data = this.source.firstChild.innerHTML;if (!nitobi.browser.IE) {

//focus back control when the copy is completeattachEvent(this.clipboard, "keyup",

this.focus);//set the value, focus and select the valuethis.clipboard.value = data;this.clipboard.focus();

this.clipboard.setSelectionRange(0,data.length);} else {

window.clipboardData.setData("Text",data);}

}

CommunityOne :: May 5 :: 2008

Pastepaste: function() {

//catch the textarea keyup to grab the resultsnitobi.html.attachEventOnce(this.clipboard, "keyup", this.pasteReady);this.clipboard.focus();

},

pasteReady: function() {//get the clipboard value and insert itthis.source.firstChild.innerHTML = this.clipboard.value;//focus back on the controlthis.source.firstChild.focus();

}

CommunityOne :: May 5 :: 2008

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

Live Scrolling

• What scroll through large datasets• When viewing, filtering, sorting• Why remove page refreshes

CommunityOne :: May 5 :: 2008

How

1. Create clipping <div> and surface <div> where data goes

2. Repeat for a scrollbar3. Connect scrollbar scroll events to

position the data surface4. Retrieve data from the server as

user scrolls

CommunityOne :: May 5 :: 2008

CommunityOne :: May 5 :: 2008

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

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;"

}

#viewport {position: relative;overflow: hidden;width: 300px;height: 300px;border: 1px solid black;

}

#surface {width: 2000px;height: 2000px;

}

CommunityOne :: May 5 :: 2008

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

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

CommunityOne :: May 5 :: 2008

Acknowledgements

• Bill Scott• Michael Mahemoff• Andre Charland

CommunityOne :: May 5 :: 2008

Thank You

Questions?

email: dave@nitobi.comblog: blogs.nitobi.com/davebook: enterpriseajax.comtweets: twitter.com/davejohnson

CommunityOne :: May 5 :: 2008

http://flickr.com/photos/daveknapik/2115474105/http://flickr.com/photos/preciouskhyatt/2153351428/http://flickr.com/photos/sandcastlematt/403101240/