Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Crafting App Interfaces with jQuery

Crafting App Interfaces with jQuery

Slides from a talk I gave at DrupalCon Chicago in 2011.

Nathan Smith

March 02, 2012
Tweet

More Decks by Nathan Smith

Other Decks in Programming

Transcript

  1. 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
  2. “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.
  3. 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.
  4. 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.
  5. Co-author JavaScript books I’ve worked on... Tech editor Tech editor

    jQueryEnlightenment.com oreilly.com/catalog/9780596159788 JavaScriptEnlightenment.com
  6. 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; }
  7. 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)
  8. 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
  9. 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
  10. “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
  11. 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.
  12. <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
  13. 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()
  14. // 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.
  15. .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. });
  16. 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
  17. 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
  18. // 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
  19. // 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
  20. // 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
  21. // 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
  22. // 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
  23. // 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”
  24. // 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
  25. “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...
  26. select { -webkit-appearance: none; } WebKit’s form styling secret sauce

    This gives you back a bit of control for things like <select> drop-downs.
  27. 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>
  28. 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
  29. <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> Pre-load JavaScript files http://formalize.me/demo.html
  30. <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> Pre-load JavaScript files http://formalize.me/demo.html
  31. 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.
  32. 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
  33. 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! :)
  34. 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
  35. // FORMALIZE.init.detect_webkit detect_webkit: function() { ! if (!WEBKIT) { !

    ! return; ! } ! // Tweaks for Safari + Chrome. ! $('html').addClass('is_webkit'); }, ... Detecting WebKit
  36. // 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>'); }, ... Basic structure of Formalize JS
  37. // 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 <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 styling hooks for IE6
  38. // FORMALIZE.init.autofocus autofocus: function() { ! if (AUTOFOCUS_SUPPORTED || !$(':input[autofocus]').length)

    { ! ! return; ! } ! $(':input[autofocus]:visible:first').focus(); }, ... Adding HTML5 autofocus support
  39. // 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); ! ! }); ! }); } Adding HTML5 placeholder support
  40. // 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
  41. 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
  42. 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
  43. For questions – Or just to say hi :) Contact

    – http://sonspring.com/contact Twitter – http://twitter.com/nathansmith http://www.slideshare.net/nathansmith/drupalcon-jquery
  44. What did you think? Locate this session on the DCC

    website: http://chicago2011.drupal.org/sessions Click the “Take the Survey” link. Thanks!