drupalcon jquery

55
CRAFTING APP INTERFACES WITH Presented by Nathan Smith

Upload: nathan-smith

Post on 28-Jan-2015

122 views

Category:

Design


0 download

DESCRIPTION

Presentation from DrupalCon 2011 in Chicago

TRANSCRIPT

Page 1: DrupalCon jQuery

CRAFTING APPINTERFACES WITH

Presented by Nathan Smith

Page 2: DrupalCon jQuery

Note: Don’t feel like you need to write all of this down. The slides are available here...

http://www.slideshare.net/nathansmith/drupalcon-jquery

Carpal Tunnel Relief

Page 3: DrupalCon jQuery

“Allow myself to... introduce myself”

I work (from home) for HPas a front-end developer.

I’m best known for making a helpful 5.6 KB CSS file.

Page 4: DrupalCon jQuery

Shh... My secret in-house code reviewer

Note: This was shot hastily with a camera phone. I make no claims of being a decent photographer.

Page 5: DrupalCon jQuery

FYI – I don’t especially love CSS

The reason I create and use CSS frameworks is because I hate doing mundane tasks repeatedly (yawn).

I’d rather be working in JavaScript.

Page 6: DrupalCon jQuery

Co-author

JavaScript books I’ve worked on...

Tech editor Tech editor

jQueryEnlightenment.com

oreilly.com/catalog/9780596159788

JavaScriptEnlightenment.com

Page 7: DrupalCon jQuery

Our team at HP uses Sassto expedite writing CSS

http://www.sass-lang.com

Page 8: DrupalCon jQuery

Sass compiles down to CSS...

$blue: #3bbfce$margin: 20px

#foo border-color: $blue color: darken($blue, 9%) .bar border-color: $blue padding: $margin / 2 margin: $margin / 2

#foo { border-color: #3bbfce; color: #2b9eab;}

#foo .bar { border-color: #3bbfce; padding: 10px; margin: 10px;}

Page 9: DrupalCon jQuery

Assuming you already know CSS,Sass can help you write CSS faster.

But using Sass doesn’t magically create “better” CSS. If you write bad code, Sass won’t remedy it.

But Sass isn’t a magic bullet(Because no framework is)

Page 10: DrupalCon jQuery

The same principlesapply to JavaScript

An important discipline when using any framework is striving to understand the underlying language. In other words, useit as a tool – Not a black box

Page 11: DrupalCon jQuery

Veteran “ninjas” master a variety of tools – Not just one.

Use a framework as an extensionof yourself – Not just as a crutch.

BY HAND

FRAMEWORK

http://imdb.com/title/tt1046173

Page 12: DrupalCon jQuery

“JavaScript is the only language that I’m aware of that people feel they don’t need to learn before they start using it.”

— Douglas Crockford

http://www.youtube.com/watch?v=hQVTIJBZook

A day to learn, A lifetime to master

Page 13: DrupalCon jQuery

Today, I’ll discuss two examples...

jQuery Desktop Formalize.Me

http://desktop.sonspring.com http://formalize.me

Page 14: DrupalCon jQuery

jQuery DesktopFun with z-index

Page 15: DrupalCon jQuery

jQuery DesktopFun with z-index

Page 16: DrupalCon jQuery

Demystifying z-index

http://sonspring.com/journal/jquery-desktop

Page 17: DrupalCon jQuery

Demystifying z-index

The default z-index value is 0, and it only takes effect if an element is position: fixed, relative, or absolute. The default is position: static.

You don’t have to go overboard with z-index: 99999. Just +1 higher than another element will suffice.

Page 18: DrupalCon jQuery

<body>...<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script><script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js"></script><script> if (!window.jQuery) { document.write(unescape('%3Cscript src="assets/javascripts/jquery.js"%3E%3C/script%3E')); document.write(unescape('%3Cscript src="assets/javascripts/jquery.ui.js"%3E%3C/script%3E')); }</script><script src="assets/javascripts/jquery.desktop.js"></script>...</body>

CDN & Local Fallback=> Best of both worlds

http://desktop.sonspring.com

Page 19: DrupalCon jQuery

Singleton Pattern = Object-oriented JS

Nested JS objects versus dot-notation=> Same result, but different approaches

var APP = { foo: function() { // Do stuff. }, bar: function() { // Do more stuff. }}

var APP = {};

APP.foo = function() { // Do stuff.};

APP.bar = function() { // Do more stuff.};

I prefer the one on the left, but you would call methods byusing the same syntax either way: APP.foo() + APP.bar()

Page 20: DrupalCon jQuery

// Module pattern.var APP = (function($, window, document, undefined) { // For use only inside APP. var PRIVATE_CONSTANT_1 = 'foo'; var PRIVATE_CONSTANT_2 = 'bar';

// Expose contents of APP. return { go: function() { for (var i in APP.init) { APP.init[i](); } }, // APP.init init: { call_automatically: function() { // Called when DOM is ready. // Can still be called individually, via: // APP.init.call_automatically(); }, // etc. }, // APP.misc misc: { call_specifically: function() { // Must be called individually, via: // APP.misc.call_specifically(); }, // etc. } };// Alias window, document.})(jQuery, this, this.document);

// Automatically run all functions in APP.initjQuery(document).ready(function() { APP.go();});

Module Pattern << init example

Using a module pattern exposes

only one global variable, in this case “APP” – with the rest of

your methods within an object.

It gives you the ability to use private “constant” variables

inside your appʼs function scope.

Nesting functions within an “init” object allows them to be called

automatically on DOM ready.

Page 21: DrupalCon jQuery

.live() is Awesome. Use it. Love it.$('div.window').live('mousedown', function() { // Bring window to front. JQD.util.window_flat(); $(this).addClass('window_stack');}).live('mouseenter', function() { $(this).die('mouseenter').draggable({ // Confine to desktop. // Movable via top bar only. cancel: 'a', containment: 'parent', handle: 'div.window_top' }).resizable({ containment: 'parent', minWidth: 400, minHeight: 200 });

// etc.});

Page 22: DrupalCon jQuery

All the event wire-ups are ready, before the page even has any content. Once the content is loaded remotely via Ajax, the elements respond to their respective predefined .live() event listeners.

Live (pun intended) example of .live()

http://desktop.sonspring.com/ajax_load.html

Page 23: DrupalCon jQuery

var JQD = (function($, window, document, undefined) { return { go: function() { for (var i in JQD.init) { JQD.init[i](); } }, init: { frame_breaker: function() {/* ... */}, clock: function() {/* ... */}, desktop: function() {/* ... */}, wallpaper: function() {/* ... */} }, util: { clear_active: function() {/* ... */}, window_flat: function() {/* ... */}, window_resize: function() {/* ... */} }})(jQuery, this, this.document);

Basic structure of jQuery Desktop

Page 24: DrupalCon jQuery

// Cancel mousedown, right-click.$(document).mousedown(function(ev) { var tags = ['a', 'button', 'input', 'select', 'textarea'];

if (!$(ev.target).closest(tags).length) { JQD.util.clear_active(); ev.preventDefault(); ev.stopPropagation(); }}).bind('contextmenu', function() { return false;});

Event listeners at the document level

Page 25: DrupalCon jQuery

// Relative or remote links?$('a').live('click', function(ev) { var url = $(this).attr('href'); this.blur();

if (url.match(/^#/)) { ev.preventDefault(); ev.stopPropagation(); } else { $(this).attr('target', '_blank'); }});

External links open in a new window

Page 26: DrupalCon jQuery

// Make top menus active.$('a.menu_trigger').live('mousedown', function() { if ($(this).next('ul.menu').is(':hidden')) { JQD.util.clear_active(); $(this).addClass('active').next('ul.menu').show(); } else { JQD.util.clear_active(); }}).live('mouseenter', function() { // Transfer focus, if already open. if ($('ul.menu').is(':visible')) { JQD.util.clear_active(); $(this).addClass('active').next('ul.menu').show(); }});

Top level drop-down menus

Page 27: DrupalCon jQuery

// Desktop icons.$('a.icon').live('mousedown', function() { // Highlight the icon. JQD.util.clear_active(); $(this).addClass('active');}).live('dblclick', function() { // Get the link's target. var x = $(this).attr('href'); var y = $(x).find('a').attr('href');

// Show the taskbar button. if ($(x).is(':hidden')) { $(x).remove().appendTo('#dock'); $(x).show('fast'); }

// Bring window to front. JQD.util.window_flat(); $(y).addClass('window_stack').show();}).live('mouseenter', function() { $(this).die('mouseenter').draggable({ revert: true, containment: 'parent' });});

Making desktop icons interactive

Page 28: DrupalCon jQuery

// Taskbar buttons.$('#dock a').live('click', function() { // Get the link's target. var x = $($(this).attr('href'));

// Hide, if visible. if (x.is(':visible')) { x.hide(); } else { // Bring window to front. JQD.util.window_flat(); x.show().addClass('window_stack'); }});

Taskbar / Dock buttons

Page 29: DrupalCon jQuery

// Minimize the window.$('a.window_min').live('click', function() { $(this).closest('div.window').hide();});

// Maximize or restore the window.$('a.window_resize').live('click', function() { JQD.util.window_resize(this);});

// Close the window.$('a.window_close').live('click', function() { $(this).closest('div.window').hide(); $($(this).attr('href')).hide('fast');});

Manipulating the “windows”

Page 30: DrupalCon jQuery

// Show desktop button, ala Windows OS.$('#show_desktop').live('click', function() { // If any windows are visible, hide all. if ($('div.window:visible').length) { $('div.window').hide(); } else { // Otherwise, reveal hidden windows that are open. $('#dock li:visible a').each(function() { $($(this).attr('href')).show(); }); }});

Clicking the Show Desktop button

Page 31: DrupalCon jQuery

To prevent developers fromwasting countless hours onstyling dumb form elements

Page 32: DrupalCon jQuery

“Future plans include a tutorial on how to use jQuery to add styling hooks to form elements, since I know from experience that is no cup of tea.”

— Source = Me when announcing 960.gs in 2008!

— Excuse = New HTML5 elements set me back :)

It’s been awhile in the making...

Page 33: DrupalCon jQuery
Page 34: DrupalCon jQuery

select { -webkit-appearance: none;}

WebKit’s form styling secret sauce

This gives you back a bit of control for things like <select> drop-downs.

Page 35: DrupalCon jQuery

document.write(ad) with position:absolute

Page 36: DrupalCon jQuery

document.write(ad) with position:absolute

Page 37: DrupalCon jQuery

document.write(ad) with position:absolute

<script> document.write(unescape('%3Cscr' + 'ipt src="http://adn.fusionads.net/...' + Math.floor(Math.random() * 99999999999) + '"%3E%3C/script%3E'))</script><span id="fusion_link"> <a href="http://fusionads.net/">Ads by Fusion</a></span>...</body>

Page 38: DrupalCon jQuery

header[role="banner"] h1 { background-repeat: no-repeat; background-position: center 20px; background-image: url(../images/logo.png); background-image: url(data:imagepng;base64, iVBORw0KGgoAAA...); color: transparent; font-size: 0; overflow: hidden; text-indent: -99999px; height: 130px;}

Base64 encode background images

http://formalize.me http://www.motobit.com/util/base64-decoder-encoder.asp

Page 39: DrupalCon jQuery

<body>...

<!-- Pre-load JavaScript libraries --><script src="http://ajax.googleapis.com/ajax/libs/dojo/1.5.0/dojo/dojo.xd.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/ext-core/3.1.0/ext-core.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/mootools/1.3.0/mootools-yui-compressed.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script> <script src="http://yui.yahooapis.com/3.3.0/build/simpleyui/simpleyui-min.js"></script> <!-- Pre-load Formalize files --> <script src="assets/javascripts/dojo.formalize.js"></script> <script src="assets/javascripts/extjs.formalize.js"></script> <script src="assets/javascripts/jquery.formalize.js"></script> <script src="assets/javascripts/mootools.formalize.js"></script> <script src="assets/javascripts/prototype.formalize.js"></script> <script src="assets/javascripts/yui.formalize.js"></script> </body>

Pre-load JavaScript files

http://formalize.me/demo.html

Page 40: DrupalCon jQuery

<body>...

<!-- Pre-load JavaScript libraries --><script src="http://ajax.googleapis.com/ajax/libs/dojo/1.5.0/dojo/dojo.xd.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/ext-core/3.1.0/ext-core.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/mootools/1.3.0/mootools-yui-compressed.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script> <script src="http://yui.yahooapis.com/3.3.0/build/simpleyui/simpleyui-min.js"></script> <!-- Pre-load Formalize files --> <script src="assets/javascripts/dojo.formalize.js"></script> <script src="assets/javascripts/extjs.formalize.js"></script> <script src="assets/javascripts/jquery.formalize.js"></script> <script src="assets/javascripts/mootools.formalize.js"></script> <script src="assets/javascripts/prototype.formalize.js"></script> <script src="assets/javascripts/yui.formalize.js"></script> </body>

Pre-load JavaScript files

http://formalize.me/demo.html

Page 41: DrupalCon jQuery

var IE6 = IE(6);var IE7 = IE(7);

// Internet Explorer detection.function IE(version) { var b = document.createElement('b'); b.innerHTML = '<!--[if IE ' + version + ']><br><![endif]-->'; return !!b.getElementsByTagName('br').length;}

IE detection for Prototype.js

http://formalize.me/assets/javascripts/prototype.formalize.js

Prevents false positives since Opera can be made to impersonate IE.Note: I did this because Prototype doesn’t detect IE version number.

Page 42: DrupalCon jQuery

var ie = (function() { var undef, v = 3, div = document.createElement('div'), all = div.getElementsByTagName('i');

while ( div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0] );

return v > 4 ? v : undef;}());

James Padolsey's IE detection (whoa!)

https://gist.github.com/527683

Page 43: DrupalCon jQuery

Line number counts in Formalize JS

Dojo = 174 lines ExtJS = 168 lines

jQuery = 158 lines MooTools = 163 lines

Prototype = 171 lines YUI = 168 lines

“Write Less, Do More” FTW! :)

Page 44: DrupalCon jQuery

var FORMALIZE = (function($, window, document, undefined) { // Private constants.! var PLACEHOLDER_SUPPORTED = 'placeholder' in document.createElement('input');! var AUTOFOCUS_SUPPORTED = 'autofocus' in document.createElement('input');! var WEBKIT = 'webkitAppearance' in document.createElement('select').style;! var IE6 = !!($.browser.msie && parseInt($.browser.version, 10) === 6);! var IE7 = !!($.browser.msie && parseInt($.browser.version, 10) === 7);

// Expose innards of FORMALIZE. return { go: function() {/* ... */}, init: { detect_webkit: function() {/* ... */}, full_input_size: function() {/* ... */}, ie6_skin_inputs: function() {/* ... */}, autofocus: function() {/* ... */}, placeholder: function() {/* ... */} }, misc: { add_placeholder: function() {/* ... */} } };})(jQuery, this, this.document);

Basic structure of Formalize JS

Page 45: DrupalCon jQuery

// FORMALIZE.init.detect_webkitdetect_webkit: function() {! if (!WEBKIT) {! ! return;! }

! // Tweaks for Safari + Chrome.! $('html').addClass('is_webkit');},

...

Detecting WebKit

Page 46: DrupalCon jQuery

// FORMALIZE.init.full_input_sizefull_input_size: function() {! if (!IE7 || !$('textarea, input.input_full').length) {! ! return;! }

! // This fixes width: 100% on <textarea> and class="input_full".! // It ensures that form elements don't go wider than container.! $('textarea, input.input_full') .wrap('<span class="input_full_wrap"></span>');},

...

Basic structure of Formalize JS

Page 47: DrupalCon jQuery

// FORMALIZE.init.ie6_skin_inputsie6_skin_inputs: function() { // Test for Internet Explorer 6. if (!IE6 || !$('input, select, textarea').length) { // Exit if the browser is not IE6, // or if no form elements exist. return; }

// For <input type="submit" />, etc. var button_regex = /button|submit|reset/;

// For <input type="text" />, etc. var type_regex = /date|datetime|datetime-local|email|month|number|password|range|search|tel|text|time|url|week/;

$('input').each(function() { var el = $(this);

// Is it a button? if (this.getAttribute('type').match(button_regex)) { el.addClass('ie6_button');

/* Is it disabled? */ if (this.disabled) { el.addClass('ie6_button_disabled'); } } // Or is it a textual input? else if (this.getAttribute('type').match(type_regex)) { el.addClass('ie6_input');

/* Is it disabled? */ if (this.disabled) { el.addClass('ie6_input_disabled'); } } });

$('textarea, select').each(function() { /* Is it disabled? */ if (this.disabled) { $(this).addClass('ie6_input_disabled'); } });},

Adding stylinghooks for IE6

Page 48: DrupalCon jQuery

// FORMALIZE.init.autofocusautofocus: function() {! if (AUTOFOCUS_SUPPORTED || !$(':input[autofocus]').length) {! ! return;! }

! $(':input[autofocus]:visible:first').focus();},

...

Adding HTML5 autofocus support

Page 49: DrupalCon jQuery

// FORMALIZE.init.placeholderplaceholder: function() {! if (PLACEHOLDER_SUPPORTED || !$(':input[placeholder]').length) {! ! // Exit if placeholder is supported natively,! ! // or if page does not have any placeholder.! ! return;! }

! FORMALIZE.misc.add_placeholder();

! $(':input[placeholder]').each(function() {! ! var el = $(this);! ! var text = el.attr('placeholder');

! ! el.focus(function() {! ! ! if (el.val() === text) {! ! ! ! el.val('').removeClass('placeholder_text');! ! ! }! ! }).blur(function() {! ! ! FORMALIZE.misc.add_placeholder();! ! });

! ! // Prevent <form> from accidentally! ! // submitting the placeholder text.! ! el.closest('form').submit(function() {! ! ! if (el.val() === text) {! ! ! ! el.val('').removeClass('placeholder_text');! ! ! }! ! }).bind('reset', function() {! ! ! setTimeout(FORMALIZE.misc.add_placeholder, 50);! ! });! });}

Adding HTML5 placeholder support

Page 50: DrupalCon jQuery

// FORMALIZE.miscmisc: {! // FORMALIZE.misc.add_placeholder! add_placeholder: function() {! ! if (PLACEHOLDER_SUPPORTED || !$(':input[placeholder]').length) {! ! ! // Exit if placeholder is supported natively,! ! ! // or if page does not have any placeholder.! ! ! return;! ! }

! ! $(':input[placeholder]').each(function() {! ! ! var el = $(this);! ! ! var text = el.attr('placeholder');

! ! ! if (!el.val() || el.val() === text) {! ! ! ! el.val(text).addClass('placeholder_text');! ! ! }! ! });! }}

Moved outside of init, for easier reuse

Page 52: DrupalCon jQuery

jQuery Companion Libraries

jQuery UI – http://jqueryui.comjQuery Mobile – http://jquerymobile.com

Amplify – http://amplifyjs.comUnderscore – http://documentcloud.github.com/underscore

Backbone – http://documentcloud.github.com/backbone

JS TestingJasmine – http://pivotal.github.com/jasmine

QUnit – http://docs.jquery.com/Qunit

If you want write Ruby-esque code that compiles to JS...CoffeeScript – http://jashkenas.github.com/coffee-script

Additional Resources

Page 53: DrupalCon jQuery

Learning jQuery – https://www.packtpub.com/learning-jquery-1.3

jQuery Enlightenment – http://jqueryenlightenment.comDOM Scripting – http://domscripting.com

JavaScript: The Good Parts – http://oreilly.com/catalog/9780596517748JavaScript: The Definitive Guide – http://oreilly.com/catalog/9780596101992

High Performance JavaScript – http://oreilly.com/catalog/9780596802806Pro JavaScript Design Patterns – http://jsdesignpatterns.com

Object-Oriented JavaScript – https://www.packtpub.com/object-oriented-javascript/bookThe Art and Science of JavaScript – http://www.sitepoint.com/books/jsdesign1

Test-Driven JavaScript Development – http://tddjs.com

Recommended Books

Page 54: DrupalCon jQuery

For questions – Or just to say hi :)

Contact – http://sonspring.com/contact

Twitter – http://twitter.com/nathansmith

http://www.slideshare.net/nathansmith/drupalcon-jquery

Page 55: DrupalCon jQuery

What did you think?

Locate this session on the DCC website:http://chicago2011.drupal.org/sessions

Click the “Take the Survey” link.

Thanks!