Presentation: Embracing and celebrating redundancy

Embracing and celebrating redundancy

redundant keys

Christian Heilmann, Mozilla, jQuery UK confernce, February 2012

Today I am here at a jQuery conference talking to people who are all about jQuery and know their way around it. I remember when jQuery came out and amongst other libraries promised first and foremost one thing: make it much easier to develop cross-browser and in general allow us to "write less and

achieve more".

Bad browsers gave us a few options

  • Cater to the lowest common denominator?
  • Block out old browsers?
  • Abstract browser differences into a library?

This was absolutely necessary, as browsers back then sucked. We had three options: either we catered to the lowest common denominator and build very basic interfaces (that are slow and multi-step where one step could have done the job) or we could have blocked out old browsers as a whole or we could have abstracted the differences in browsers out into a library.

jQuery chose the third option

  • Create an API that works around browser differences
  • Introduce chaining
  • Replace the DOM
  • Access everything with CSS selectors

jQuery went with the third option and abstracted the issues out. This is great. Then it went further and even abstracted the whole language out into a different syntax and replaced the seemingly unwieldy DOM with a faster way of accessing content in the page via CSS selectors.

jQuery simulates the browser we wanted

  • Specialised for content manipulation
  • Easy interface effects and animations
  • Easier event handling
  • Ajax without the X (and its complexities)

In essence, jQuery simulated a browser that is catered for DOM manipulations and interface effects. Later on jQuery also made Ajax easier and provided us with a better and more solid event facade in the browser.

jQuery is yummy and lean!

Bunny eating a carrot

Abstraction != Obstruction

The happy vegans

The problem is that when you abstract the underlying issues out into a different solution you can become dependent on this solution and we are getting there right now.

market.shift()

Boston globe on several devices

The other issue is that whilst the cross-browser issue still is there, our market has moved to include mobiles and tablets and jQuery is not the right solution for those environments as it solves issues that are of less importance there.

Just imagine…

  • What if browsers didn't suck any more?
  • And what if that already happened?

jQuery.first() != browser.innovation

  • jQuery won! You showed a better way
  • Now it is time to reap the rewards
  • And time to move on and change the native web tech

So today I want to give a few pointers and reminders that browsers these days do in fact not suck any longer, but - learning from the success of jQuery - moved on, too and by advocating "jQuery first" we actually hurt innovation and teach practices in development that are easy to apply but not really necessary.

'\\'.replace( 's', '' );

from jQuery to vanilla JavaScript

A few weeks ago there was a blog post that showed a backlash advocating using "normal JavaScript" in favour of jQuery called From jQuery to JavaScript: A Reference. The author Jeffrey Way showed how common jQuery use cases can be done with JavaScript that is not much longer than its jQuery equivalent. This also resulted in a handy cheatsheet.

Native = good. Usecase.shift() = better

[].forEach.call(document.querySelectorAll("a"), function(el) { el.addEventListener("click", function() { // code… }); });

This is good and interesting but I actually think we can go even further. The concepts Jeffrey showed explain some of the things browsers have right now more advanced interfaces for, but it also showed some use cases that jQuery excels at but are not necessarily a good idea seeing how the rest of the web technologies moved on. By understanding and using what browsers do, we can actually avoid a lot of the "problems" jQuery excels at solving.

$('#all.the.things').css('change','them');

  • Very tight bond between structure and behaviour
  • Lots of DOM walking (slow)
  • Presentation on the behaviour layer

One big thing new jQuery users get very excited about is how easy it is to find and manipulate elements by CSS selector and then change their look. This is tempting but also annoying. Effectively what you do with that is put together the worst ideas for web development:

  • You maintain a very tight bond between structure and behaviour
  • You navigate the DOM a lot (which is slow - especially in legacy browsers)
  • You put presentation into the JavaScript (behaviour) layer

Replacing short but complex code with planning

Reusing a calendar

In the past, it was necessary to simulate what browsers should do for us. But these days, and by applying concepts rooted in proper understanding of how things work in browsers we don't need that any longer.

A nice little to-do-list

Let's take a look at an example. How about a to-do list?

Using the in-built $

var todo = document.querySelector( '#todolist' ), form = document.querySelector( 'form' ), field = document.querySelector( '#newitem' );

document.querySelector is much like the $ selector in jQuery. You can give it any CSS selector and it returns the node. querySelectorAll returns a node list.

Form submission

form.addEventListener( 'submit', function( ev ) { todo.innerHTML += '<li>' + field.value + '</li>'; todo.lastChild.tabIndex = -1; field.value = ''; field.focus(); storestate(); ev.preventDefault(); }, false);

By listening for the submit handler we support keyboard and mouse.

List item tick and removal

todo.addEventListener( 'click', function( ev ) { var t = ev.target; if ( t.tagName === 'LI' ) { if ( t.classList.contains( 'done' ) ) { t.parentNode.removeChild( t ); } else { t.classList.add( 'done' ); } storestate(); }; ev.preventDefault(); }, false);

We add one listener to the whole list and check that the clicked item was an LI we then add the class called 'done' the first time it is clicked and remove the list item the second time it is clicked.

Storing and retrieving state

document.addEventListener( 'DOMContentLoaded', retrievestate, false ); function storestate() { localStorage.todolist = todo.innerHTML; }; function retrievestate() { if ( localStorage.todolist ) { todo.innerHTML = localStorage.todolist; } };

We use local storage to cache the whole interface for the next page load.

Visual candy

#todolist li:hover:after, #todolist li:focus:after, #todolist li.done:after { content: '✔'; color: #060; position: absolute; right: 5px; } #todolist li.done:hover:after, #todolist li.done:focus:after { content: 'x'; font-weight: bold; color: #c00; position: absolute; right: 5px; }

The indicators of the state of the list item are generated in CSS, no need to mess with the DOM at all.

Techniques used to write less

  • Event delegation instead of loops
  • Adding classes to make the CSS parser navigate the DOM
  • Generation of visual fluff in CSS

Event delegation and understanding the submit handler made it possible to have a very short JavaScript functionality. By adding and removing classes we gave the CSS access to our functionality and by generating visual content in CSS we avoided having to create them in CSS or have non-sensical HTML in the document.

Smooth…

Another big use case of jQuery is visual effects. Let's take a look at another example that achieves them using no need to animate at all.

Sensible HTML structures

<nav> <ul> <li><a href="#cafe">The Cafe</a></li> <li><a href="#fashion">Fashion</a></li> <li><a href="#food">Food</a></li> <li><a href="#gifts">Gifts</a></li> </ul> </nav> </header> <section> <article id="cafe">[...]</article> <article id="fashion">[...]</article> <article id="food">[...]</article> <article id="gifts">[...]</article> </section>

Here we go even further and use already existing connections in the document - links pointing at ID targets to our advantage.

JS hooks and adding classes

document.body.classList.add( 'js' ); var nav = document.querySelector( 'nav' ), article = document.querySelector( 'article' ), link = document.querySelector( 'nav a' ); link.classList.add( 'current' ); article.classList.add( 'current' ); CSS: body.js article { width: 700px; position: absolute; top: -700px; -webkit-transition: 0.8s;-moz-transition: 0.8s; -ms-transition: 0.8s;-o-transition: 0.8s; transition: 0.8s; } body.js article.current { position: absolute; top: 0; }

Instead of defining fixed visual style changes in JavaScript we just apply classes and leave the rest to the CSS and transitions.

Reusing two variables with the current state

nav.addEventListener( 'click', function( ev ) { var t = ev.target; if ( t.tagName === 'A' ) { article.classList.remove( 'current' ); link.classList.remove( 'current' ); article = document.querySelector( t.getAttribute( 'href' ) ); link = t; article.classList.add( 'current' ); link.classList.add( 'current' ); } }, false);

There is no need to navigate the DOM as the link clicked has the selector we need as its href attribute.

Bookmarking and state?

if ( document.location.hash ) { var cleanhash = document.location.hash.replace( /^#/, '' ); article = document.querySelector( document.location.hash ); link = document.querySelector('nav a[href$=' + cleanhash + ']'); }

Using attribute selectors we can simply define the right elements to show on page load.

The trickery for small code

  • Semantic HTML that even works in IE6!
  • Animations and transitions with CSS - smooth from one state to another - hardware accelerated
  • Piggy-backing on existing connections of targets and links

The beauty and simplicity of this is that we can now use CSS for all the things visual. And the browser even does the calculations and morphing for us.

My wishes

Christian Heilmann wishing things

There are a few things I wished the jQuery community would do now.

My wishes

  • Use what browsers natively give you
  • Plan and architect solutions, don't pile effect on effect
  • Structure your code in cached elements and reusable functions
  • Don't answer any JS question with 'use jQuery'
  • Don't write plugins or add methods to cater for edge cases
  • Use what browsers have and give us feedback - let's fix the source instead of abstracting

Things I wish for from you after this is to consider the incredible power we now have in browsers instead of creating everything in jQuery. I especially implore you to count to 10 before answering people's requests in forums and other communication channels with "use jQuery" or "let me write you a plugin".

Go kick asses that need kicking!

two goats

jQuery and its community helped us a lot to move the web forward. It was the kick in the butt browser makers needed. Now it is time to reap the rewards of this instead of making jQuery a walled garden and a annoying dependency of the nearer future.

Thanks!