Slide 1

Slide 1 text

CRAFTING APP INTERFACES WITH Presented by Nathan Smith

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

“Allow myself to... introduce myself” I work (from home) for HP as a front-end developer. I’m best known for making a helpful 5.6 KB CSS file.

Slide 4

Slide 4 text

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.

Slide 5

Slide 5 text

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.

Slide 6

Slide 6 text

Co-author JavaScript books I’ve worked on... Tech editor Tech editor jQueryEnlightenment.com oreilly.com/catalog/9780596159788 JavaScriptEnlightenment.com

Slide 7

Slide 7 text

Our team at HP uses Sass to expedite writing CSS http://www.sass-lang.com

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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)

Slide 10

Slide 10 text

The same principles apply to JavaScript An important discipline when using any framework is striving to understand the underlying language. In other words, use it as a tool – Not a black box

Slide 11

Slide 11 text

Veteran “ninjas” master a variety of tools – Not just one. Use a framework as an extension of yourself – Not just as a crutch. BY HAND FRAMEWORK http://imdb.com/title/tt1046173

Slide 12

Slide 12 text

“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

Slide 13

Slide 13 text

Today, I’ll discuss two examples... jQuery Desktop Formalize.Me http://desktop.sonspring.com http://formalize.me

Slide 14

Slide 14 text

jQuery Desktop Fun with z-index

Slide 15

Slide 15 text

jQuery Desktop Fun with z-index

Slide 16

Slide 16 text

Demystifying z-index http://sonspring.com/journal/jquery-desktop

Slide 17

Slide 17 text

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.

Slide 18

Slide 18 text

... 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')); } ... CDN & Local Fallback => Best of both worlds http://desktop.sonspring.com

Slide 19

Slide 19 text

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 by using the same syntax either way: APP.foo() + APP.bar()

Slide 20

Slide 20 text

// 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.init jQuery(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.

Slide 21

Slide 21 text

.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. });

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

// 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

Slide 25

Slide 25 text

// 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

Slide 26

Slide 26 text

// 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

Slide 27

Slide 27 text

// 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

Slide 28

Slide 28 text

// 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

Slide 29

Slide 29 text

// 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”

Slide 30

Slide 30 text

// 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

Slide 31

Slide 31 text

To prevent developers from wasting countless hours on styling dumb form elements

Slide 32

Slide 32 text

“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...

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

select { -webkit-appearance: none; } WebKit’s form styling secret sauce This gives you back a bit of control for things like drop-downs.

Slide 35

Slide 35 text

document.write(ad) with position:absolute

Slide 36

Slide 36 text

document.write(ad) with position:absolute

Slide 37

Slide 37 text

document.write(ad) with position:absolute document.write(unescape('%3Cscr' + 'ipt src="http://adn.fusionads.net/...' + M Ads by Fusion ...

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

... Pre-load JavaScript files http://formalize.me/demo.html

Slide 40

Slide 40 text

... Pre-load JavaScript files http://formalize.me/demo.html

Slide 41

Slide 41 text

var IE6 = IE(6); var IE7 = IE(7); // Internet Explorer detection. function IE(version) { var b = document.createElement('b'); b.innerHTML = ''; 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.

Slide 42

Slide 42 text

var ie = (function() { var undef, v = 3, div = document.createElement('div'), all = div.getElementsByTagName('i'); while ( div.innerHTML = '', all[0] ); return v > 4 ? v : undef; }()); James Padolsey's IE detection (whoa!) https://gist.github.com/527683

Slide 43

Slide 43 text

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! :)

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

// FORMALIZE.init.detect_webkit detect_webkit: function() { ! if (!WEBKIT) { ! ! return; ! } ! // Tweaks for Safari + Chrome. ! $('html').addClass('is_webkit'); }, ... Detecting WebKit

Slide 46

Slide 46 text

// FORMALIZE.init.full_input_size full_input_size: function() { ! if (!IE7 || !$('textarea, input.input_full').length) { ! ! return; ! } ! // This fixes width: 100% on and class="input_full". ! // It ensures that form elements don't go wider than container. ! $('textarea, input.input_full') .wrap(''); }, ... Basic structure of Formalize JS

Slide 47

Slide 47 text

// FORMALIZE.init.ie6_skin_inputs ie6_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 , etc. var button_regex = /button|submit|reset/; // For , 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 styling hooks for IE6

Slide 48

Slide 48 text

// FORMALIZE.init.autofocus autofocus: function() { ! if (AUTOFOCUS_SUPPORTED || !$(':input[autofocus]').length) { ! ! return; ! } ! $(':input[autofocus]:visible:first').focus(); }, ... Adding HTML5 autofocus support

Slide 49

Slide 49 text

// FORMALIZE.init.placeholder placeholder: 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 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

Slide 50

Slide 50 text

// FORMALIZE.misc misc: { ! // 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

Slide 51

Slide 51 text

http://jeromeetienne.github.com/jquery-mobile-960

Slide 52

Slide 52 text

jQuery Companion Libraries jQuery UI – http://jqueryui.com jQuery Mobile – http://jquerymobile.com Amplify – http://amplifyjs.com Underscore – http://documentcloud.github.com/underscore Backbone – http://documentcloud.github.com/backbone JS Testing Jasmine – 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

Slide 53

Slide 53 text

Learning jQuery – https://www.packtpub.com/learning-jquery-1.3 jQuery Enlightenment – http://jqueryenlightenment.com DOM Scripting – http://domscripting.com JavaScript: The Good Parts – http://oreilly.com/catalog/9780596517748 JavaScript: The Definitive Guide – http://oreilly.com/catalog/9780596101992 High Performance JavaScript – http://oreilly.com/catalog/9780596802806 Pro JavaScript Design Patterns – http://jsdesignpatterns.com Object-Oriented JavaScript – https://www.packtpub.com/object-oriented-javascript/book The Art and Science of JavaScript – http://www.sitepoint.com/books/jsdesign1 Test-Driven JavaScript Development – http://tddjs.com Recommended Books

Slide 54

Slide 54 text

For questions – Or just to say hi :) Contact – http://sonspring.com/contact Twitter – http://twitter.com/nathansmith http://www.slideshare.net/nathansmith/drupalcon-jquery

Slide 55

Slide 55 text

What did you think? Locate this session on the DCC website: http://chicago2011.drupal.org/sessions Click the “Take the Survey” link. Thanks!