CRAFTING APP INTERFACES WITH Presented by Nathan Smith
Carpal Tunnel Relief 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
“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.
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.
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.
JavaScript books I’ve worked on... Co-author Tech editor Tech editor jQueryEnlightenment.com JavaScriptEnlightenment.com oreilly.com/catalog/9780596159788
Our team at HP uses Sass to expedite writing CSS http://www.sass-lang.com
Sass compiles down to CSS... $blue: #3bbfce #foo { $margin: 20px border-color: #3bbfce; color: #2b9eab; #foo } border-color: $blue color: darken($blue, 9%) #foo .bar { .bar border-color: #3bbfce; border-color: $blue padding: 10px; margin: 10px; padding: $margin / 2 } margin: $margin / 2
But Sass isn’t a magic bullet (Because no framework is) 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.
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
Veteran “ninjas” master a variety of tools – Not just one. FRAMEWORK BY H AND Use a framework as an extension of yourself – Not just as a crutch. http://imdb.com/title/tt1046173
A day to learn, A lifetime to master “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
Today, I’ll discuss two examples... jQuery Desktop Formalize.Me http://desktop.sonspring.com http://formalize.me
jQuery Desktop Fun with z-index
jQuery Desktop Fun with z-index
Demystifying z-index http://sonspring.com/journal/jquery-desktop
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.
CDN & Local Fallback => Best of both worlds <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> http://desktop.sonspring.com
Singleton Pattern = Object-oriented JS Nested JS objects versus dot-notation => Same result, but different approaches var APP = {}; var APP = { foo: function() { APP.foo = function() { // Do stuff. // Do stuff. }, }; bar: function() { // Do more 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()
// Module pattern. Module Pattern var APP = (function($, window, document, undefined) { // For use only inside APP. var PRIVATE_CONSTANT_1 = 'foo'; << init example var PRIVATE_CONSTANT_2 = 'bar'; // Expose contents of APP. return { go: function() { for (var i in APP.init) { APP.init[i](); Using a module pattern exposes } }, only one global variable, in this // APP.init init: { case “APP” – with the rest of call_automatically: function() { // Called when DOM is ready. your methods within an object. // Can still be called individually, via: // APP.init.call_automatically(); }, // etc. }, It gives you the ability to use // APP.misc misc: { private “constant” variables call_specifically: function() { // Must be called individually, via: // APP.misc.call_specifically(); inside your appʼs function scope. }, // etc. } }; Nesting functions within an “init” // Alias window, document. })(jQuery, this, this.document); object allows them to be called // Automatically run all functions in APP.init jQuery(document).ready(function() { automatically on DOM ready. APP.go(); });
.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. });
Live (pun intended) example of .live() http://desktop.sonspring.com/ajax_load.html 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.
Basic structure of jQuery Desktop 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);
Event listeners at the document level // 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; });
External links open in a new window // 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'); } });
Top level drop-down menus // 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(); } });
Making desktop icons interactive // 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' }); });
Taskbar / Dock buttons // 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'); } });
Manipulating the “windows” // 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'); });
Clicking the Show Desktop button // 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(); }); } });
To prevent developers from wasting countless hours on styling dumb form elements
It’s been awhile in the making... “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 :)
WebKit’s form styling secret sauce select { -webkit-appearance: none; } This gives you back a bit of control for things like <select> drop-downs.
document.write(ad) with position:absolute
document.write(ad) with position:absolute
document.write(ad) with position:absolute <script> document.write(unescape('%3Cscr' + 'ipt src="http://adn.fusionads.net/...' + M </script> <span id="fusion_link"> <a href="http://fusionads.net/">Ads by Fusion</a> </span> ... </body>
Base64 encode background images 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; } http://formalize.me http://www.motobit.com/util/base64-decoder-encoder.asp
Pre-load JavaScript files <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 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> http://formalize.me/demo.html
Pre-load JavaScript files <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 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> http://formalize.me/demo.html
IE detection for Prototype.js Prevents false positives since Opera can be made to impersonate IE. Note: I did this because Prototype doesn’t detect IE version number. 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; } http://formalize.me/assets/javascripts/prototype.formalize.js
James Padolsey's IE detection (whoa!) 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; }()); https://gist.github.com/527683
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! :)
Basic structure of Formalize JS 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);
Detecting WebKit // FORMALIZE.init.detect_webkit detect_webkit: function() { ! if (!WEBKIT) { ! ! return; ! } ! // Tweaks for Safari + Chrome. ! $('html').addClass('is_webkit'); }, ...
Basic structure of Formalize JS // FORMALIZE.init.full_input_size full_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>'); }, ...
Adding styling // 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; hooks for IE6 } // 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 HTML5 autofocus support // FORMALIZE.init.autofocus autofocus: function() { ! if (AUTOFOCUS_SUPPORTED || !$(':input[autofocus]').length) { ! ! return; ! } ! $(':input[autofocus]:visible:first').focus(); }, ...
Adding HTML5 placeholder support // 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 <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); ! ! }); ! }); }
Moved outside of init, for easier reuse // 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'); ! ! ! } ! ! }); ! } }
http://jeromeetienne.github.com/jquery-mobile-960
Additional Resources 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
Recommended Books 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
For questions – Or just to say hi :) Contact – http://sonspring.com/contact Twitter – http://twitter.com/nathansmith http://www.slideshare.net/nathansmith/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!

DrupalCon jQuery

  • 1.
    CRAFTING APP INTERFACES WITH Presented by Nathan Smith
  • 2.
    Carpal Tunnel Relief 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
  • 3.
    “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.
  • 4.
    Shh... My secretin-house code reviewer Note: This was shot hastily with a camera phone. I make no claims of being a decent photographer.
  • 5.
    FYI – Idon’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.
  • 6.
    JavaScript books I’veworked on... Co-author Tech editor Tech editor jQueryEnlightenment.com JavaScriptEnlightenment.com oreilly.com/catalog/9780596159788
  • 7.
    Our team atHP uses Sass to expedite writing CSS http://www.sass-lang.com
  • 8.
    Sass compiles downto CSS... $blue: #3bbfce #foo { $margin: 20px border-color: #3bbfce; color: #2b9eab; #foo } border-color: $blue color: darken($blue, 9%) #foo .bar { .bar border-color: #3bbfce; border-color: $blue padding: 10px; margin: 10px; padding: $margin / 2 } margin: $margin / 2
  • 9.
    But Sass isn’ta magic bullet (Because no framework is) 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.
  • 10.
    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
  • 11.
    Veteran “ninjas” mastera variety of tools – Not just one. FRAMEWORK BY H AND Use a framework as an extension of yourself – Not just as a crutch. http://imdb.com/title/tt1046173
  • 12.
    A day tolearn, A lifetime to master “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
  • 13.
    Today, I’ll discusstwo examples... jQuery Desktop Formalize.Me http://desktop.sonspring.com http://formalize.me
  • 14.
  • 15.
  • 16.
    Demystifying z-index http://sonspring.com/journal/jquery-desktop
  • 17.
    Demystifying z-index The defaultz-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.
  • 18.
    CDN & LocalFallback => Best of both worlds <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> http://desktop.sonspring.com
  • 19.
    Singleton Pattern =Object-oriented JS Nested JS objects versus dot-notation => Same result, but different approaches var APP = {}; var APP = { foo: function() { APP.foo = function() { // Do stuff. // Do stuff. }, }; bar: function() { // Do more 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()
  • 20.
    // Module pattern. Module Pattern var APP = (function($, window, document, undefined) { // For use only inside APP. var PRIVATE_CONSTANT_1 = 'foo'; << init example var PRIVATE_CONSTANT_2 = 'bar'; // Expose contents of APP. return { go: function() { for (var i in APP.init) { APP.init[i](); Using a module pattern exposes } }, only one global variable, in this // APP.init init: { case “APP” – with the rest of call_automatically: function() { // Called when DOM is ready. your methods within an object. // Can still be called individually, via: // APP.init.call_automatically(); }, // etc. }, It gives you the ability to use // APP.misc misc: { private “constant” variables call_specifically: function() { // Must be called individually, via: // APP.misc.call_specifically(); inside your appʼs function scope. }, // etc. } }; Nesting functions within an “init” // Alias window, document. })(jQuery, this, this.document); object allows them to be called // Automatically run all functions in APP.init jQuery(document).ready(function() { automatically on DOM ready. APP.go(); });
  • 21.
    .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. });
  • 22.
    Live (pun intended)example of .live() http://desktop.sonspring.com/ajax_load.html 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.
  • 23.
    Basic structure ofjQuery Desktop 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);
  • 24.
    Event listeners atthe document level // 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; });
  • 25.
    External links openin a new window // 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'); } });
  • 26.
    Top level drop-downmenus // 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(); } });
  • 27.
    Making desktop iconsinteractive // 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' }); });
  • 28.
    Taskbar / Dockbuttons // 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'); } });
  • 29.
    Manipulating the “windows” //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'); });
  • 30.
    Clicking the ShowDesktop button // 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(); }); } });
  • 31.
    To prevent developersfrom wasting countless hours on styling dumb form elements
  • 32.
    It’s been awhilein the making... “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 :)
  • 34.
    WebKit’s form stylingsecret sauce select { -webkit-appearance: none; } This gives you back a bit of control for things like <select> drop-downs.
  • 35.
  • 36.
  • 37.
    document.write(ad) with position:absolute <script> document.write(unescape('%3Cscr' + 'ipt src="http://adn.fusionads.net/...' + M </script> <span id="fusion_link"> <a href="http://fusionads.net/">Ads by Fusion</a> </span> ... </body>
  • 38.
    Base64 encode backgroundimages 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; } http://formalize.me http://www.motobit.com/util/base64-decoder-encoder.asp
  • 39.
    Pre-load JavaScript files <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 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> http://formalize.me/demo.html
  • 40.
    Pre-load JavaScript files <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 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> http://formalize.me/demo.html
  • 41.
    IE detection forPrototype.js Prevents false positives since Opera can be made to impersonate IE. Note: I did this because Prototype doesn’t detect IE version number. 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; } http://formalize.me/assets/javascripts/prototype.formalize.js
  • 42.
    James Padolsey's IEdetection (whoa!) 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; }()); https://gist.github.com/527683
  • 43.
    Line number countsin 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! :)
  • 44.
    Basic structure ofFormalize JS 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);
  • 45.
    Detecting WebKit // FORMALIZE.init.detect_webkit detect_webkit:function() { ! if (!WEBKIT) { ! ! return; ! } ! // Tweaks for Safari + Chrome. ! $('html').addClass('is_webkit'); }, ...
  • 46.
    Basic structure ofFormalize JS // FORMALIZE.init.full_input_size full_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>'); }, ...
  • 47.
    Adding styling // 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; hooks for IE6 } // 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'); } }); },
  • 48.
    Adding HTML5 autofocussupport // FORMALIZE.init.autofocus autofocus: function() { ! if (AUTOFOCUS_SUPPORTED || !$(':input[autofocus]').length) { ! ! return; ! } ! $(':input[autofocus]:visible:first').focus(); }, ...
  • 49.
    Adding HTML5 placeholdersupport // 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 <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); ! ! }); ! }); }
  • 50.
    Moved outside ofinit, for easier reuse // 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'); ! ! ! } ! ! }); ! } }
  • 51.
  • 52.
    Additional Resources jQuery CompanionLibraries 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
  • 53.
    Recommended Books 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
  • 54.
    For questions –Or just to say hi :) Contact – http://sonspring.com/contact Twitter – http://twitter.com/nathansmith http://www.slideshare.net/nathansmith/drupalcon-jquery
  • 55.
    What did youthink? Locate this session on the DCC website: http://chicago2011.drupal.org/sessions Click the “Take the Survey” link. Thanks!