Estelle Weyl | @estellevw | Github | Press to advance, 2 for comments, 4 to read/write notes.

You don't need
a framework
for that

estelle.github.com/jsframeworks

Me: Estelle Weyl

www.standardista.com | @estellevw | @standardista

HTML5 and CSS3 for the Real WorldHTML5: The Definitive GuideMObile HTML5Web Performance Daybook

As a consultant, all views are those of my employer, and not necessarily those of the speaker

My First Framework

My old portfolio

The code

var isDHTML, isID, isAll, isLayers;

if (document.getElementById) {
  isID = 1; isDHTML = 1;
} else {
  if (document.all) {
    isAll = 1; isDHTML = 1;
  } else {
    browserVersion = parseInt(navigator.appVersion);
    if ((navigator.appName.indexOf('Netscape') != -1) && (browserVersion == 4)) {
      isLayers = 1; isDHTML = 1;
    }
   }
}

function findDOM(objectID,withStyle) {
 if (withStyle == 1) {
   if (isID) { return (document.getElementById(objectID).style) ; }
   else { 
     if (isAll) { return (document.all[objectID].style); }
     else {
        if (isLayers) { return (document.layers[objectID]); }
     };
   }
 } else {
    if (isID) { return (document.getElementById(objectID)); 
    } else { 
       if (isAll) { 
          return (document.all[objectID]); 
       } else {
            if (isLayers) { return (document.layers[objectID]); }
       };
    }
  }
}

function setClass(objectID,newClass) {
    var dom =findDOM(objectID, 0);
    dom.className = newClass;
}

Why frameworks?

Normalize browser features / code once, works everywhere

if (el.addEventListener) { // W3C
  el.addEventListener("click", 
     func, false);
} // IE5+, Opera
else if (el.attachEvent) {
  el.attachEvent ("click", func);
} 
else { // Netscape 4, IE5Mac
  el.onclick = func;
} 

addEventListener supported in IE9 & Opera 7

Frameworks

  • YUI
  • jQuery
  • Dojo
  • MooTools
  • Prototype
  • Script.aculo.us
  • Ext JS
  • Google Toolkit
  • SproutCore
  • Backbone
  • QooxDoo
  • Spry
  • Cappuccino
  • Glow
  • MochiKit
  • Rialto
  • Rico
  • UIZE

Mobile Frameworks

  • Sencha Touch
  • jQuery Mobile
  • Jo
  • DHTMLX Touch / DynamicX
  • Zeptos
  • The M Project
  • XUI
  • Embed
  • Mobilize
  • ChocolateChip Mobile
  • jQTouch
  • iUI / iUIPad
  • Wink Toolkit
  • iWebKit
  • LawnChair
  • Titanium
  • PhoneGap

Frameworks
ARE Good

Don't Reinvent The Wheel

Don't reinvent the wheel

4,218 brains are better than 1

Faster, optimized, readable code

Function chaining & implicit iteration behavior

Less code. Faster to code.

Faster learning curve. No need to know JS?!?!

Seriously?

<script src="jquery.js"></script>
          
<script type="text/javascript">                                         
   $('li:first').addClass('first');     
</script>

With jQuery

$("a[href^='http']").each(function() {
  $(this).css({
    background-image: "url(http://g.etfv.co/" + this.href + ")"});
});​

with Vanilla.js

var links = 
    document.querySelectorAll("a[href^='http']"), i;

for (i=0; i<links.length; i++){
    links[i].style.backgroundImage = 
      "url(http://g.etfv.co/" + links[i].getAttribute('href') +")";
}​

jquery.min.js
from: ajax.googleapis.com/ajax/libs/jquery/1.7.2
96.2KB + HTTP request + 4J + DNS lookup

Mobile Matters

  • Minimize Battery usage
  • Reduce Latency (fewer HTTP requests)
  • Manage limited memory
  • Ensure a responsive UI
Latency

Who Killed My Battery

Download PDF

Digressing...

Mobile Performance Considerations

  • Battery Life
  • Latency
  • Memory
  • UI responsiveness
Mobile Performance presentation

3rd Party Widgets

Don't include a SPOF or cause a SPOF (Single Point of Failure)

  • global variables
  • event listeners
  • overwrite styles
  • overwrite keyboard
  • console.log
  • onError
  • alert
  • document.write
  • hover interaction
  • drag and drop
  • modal dialogues
  • block downloading
  • block rendering
  • block window.onload

Never Add Frameworks in 3rd party widgets you create!!

Vanilla JS

<script></script>

If possible, make home pages work BEFORE the library needs to be downloaded. - SurveyMonkey

While you many not support non-JavaScript users, your users are basically non-JS while they're downloading, parsing and executing your JS files

Get and maintain mad JS skillz.

Smackable!

<script src="jquery.js"></script>
          
<script type="text/javascript">                                         
   $('li:first').addClass('first');     
</script>

Natively...

Global Variables for presentation:

var d = document, i, el, EBCN, QSA;

vanilla js

d.querySelector('li').classList.add('first');

or simply

li:first-of-type {
    /* .first declarations */
}

Hit 4 for taking notes

JavaScript...

it's not always necessary

Zebra Tables

var tr = d.getElementsByTagName('tr');
for(i=0; i < tr.length; i+=2) {
	tr[i].setAttribute('class','odd');
}
thead tr {
  background-color: #999; 
  color: #fff;
}
tbody tr:nth-of-type(odd) {
  background-color: #fff;
}
tbody tr:nth-of-type(even) {
  background-color: #ccc;
}
FirstLastTwitter
EstelleWeyl@estellevw
NicoleSullivan@stubbornella
AmyHoy@amyhoy
SarahMei@sarahmei
RebeccaMurphey @rmurphey
GarannMeans@garannm
TomomiImuragirlie_mac
PamelaFox@pamelafox
StacieHibino@staciehibino
SharonMinsuk@sharonminsuk
SarahAllen@ultrasaurus
AvniKhatri@avni321
ShelleyPowers@shelleypowers

Animating without JS

nav > ul > li {
  display: inline-block; 
  position:relative;
}
nav ul ul {
  transform: scale(1,0); 
  transform-origin: top center; 
  transition: 0.2s linear 50ms; 
  position: absolute; top: 100%;
}
nav li:hover ul {
  transform: scale(1,1);
}
nav > ul > li {
  display: inline;
  zoom: 1;
  display: inline-block; 
  position:relative;
}
nav ul ul {
  -webkit-transform: scale(1,0); // all
  -ms-transform: scale(1,0);  // IE9
  -o-transform: scale(1,0);  // 12-
  transform: scale(1,0); // FF16, IE10, O-12.1
  -webkit-transform-origin: top center;
  -ms-transform-origin: top center;
  transform-origin: top center; 
  -webkit-transition: transform 0.2s linear 50ms; 
  -o-transition: transform 0.2s linear 50ms; 
  transition: transform 0.2s linear 50ms; // IE10, FF16 O12.1
  position: absolute; top: 100%;
  display: none;
}
nav li:hover ul {
  -webkit-transform: scale(1,1);
  -ms-transform: scale(1,1);
  -o-transform: scale(1,1);
  transform: scale(1,1);
  display: block;
}

Full Browser Support

CSS is Awesome...

... and so is JavaScript

getElementsByClassName

Supported since IE9

getElementsByClassName

on the document

d.getElementsByClassName('foo');
     

on a DOM node

el = document.getElementById('bar');
el.getElementsByClassName('foo');

jQuery Equivalent

el = $('.bar');

Returns a Live Node List

Try the following code in the console

var container = document.getElementById('slides');
var slides = container.getElementsByClassName('slide');
console.log('1.' + slides.length);

var foo = document.createElement('div');
foo.classList.add('slide');
container.appendChild(foo);
console.log('2.' + slides.length);
  • Live NodeList
  • Case-sensitive
  • Empty NodeList returns true
var emptyList = document.getElementsByClassName('not_found');
if(emptyList) {alert('returned empty node list')};

getElementsByClassName(names)

The getElementsByClassName(classNames) method takes a string that contains an unordered set of unique space-separated tokens representing classes. When called, the method must return a live NodeList object containing all the elements in the document, in tree order, that have all the classes specified in that argument, having obtained the classes by splitting a string on spaces. If there are no tokens specified in the argument, then the method must return an empty NodeList. If the document is in quirks mode, then the comparisons for the classes must be done in an ASCII case-insensitive manner, otherwise, the comparisons must be done in a case-sensitive manner.

-- Specification.

Selectors API

Supported since IE8

Selectors API

var chil = $('#bar .foo');

Natively

var el   = document.querySelector('#bar');

var chil = el.querySelectorAll('.foo');

or

chil = d.querySelectorAll('#bar .foo');

Selectors API

Access DOM elements with standard CSS selectors.

elem.querySelector(selector);
elem.querySelectorAll(selector);

Examples

d.querySelector('#id, li, #slides');
d.querySelectorAll('li:nth-of-type(odd)');

Spec: Selectors API level 1 | Selectors API Level 2

static nodeList

QSA = d.querySelectorAll('#slides .slide')

if you increase # of slides, EBCN increases, QSA does not.

var EBCN = d.getElementsByClassName('slide');
var QSA  = d.querySelectorAll('.slide');
var newSlide = d.createElement('div');
newSlide.classList.add('slide');
d.getElementById('slides').appendChild(newSlide);

console.log('Current EBCN: ' + EBCN.length);
console.log('Current QSA: ' + QSA.length);

No Native Iteration

jQuery

$('#slides .slide').addClass('current');

Vanilla.js

var slides = 
   d.querySelectorAll('#slides .slide');

for(var i=0; i < slides.length; i++){
   slides[i].classList.add('current');
}

classList

No Internet Explorer love :(

classList

$('#foo').addClass('bar');
$('#foo').removeClass('bar');
$('#foo').toggle('bar');
$('#foo').hasClass('bar');
d.querySelector('#foo').classList.add('bar');
d.querySelector('#foo').classList.remove('bar');
d.querySelector('#foo').classList.toggle('bar');
fooCL = d.querySelector('#foo').classList;
fooCL.add('bar');
fooCL.remove('bar');
fooCL.toggle('bar');
fooCL.contains('bar'); // true or false

classList

cl = d.querySelector('#foo').classList;
cl.add('zap');
cl.remove('bar');
cl.toggle('bar');
cl.contains('bar')); // true or false
var classes = cl.toString();
  • Chrome
  • Safari 5.1
  • Firefox
  • Opera
  • IE 10
  • mobile: Android 3, Opera 11.1

no native iterations

jQuery

$('.foo').addClass('bar');

Native

var foos = d.querySelectorAll('.foo');

for(var i = 0; i < foos.length; i++){
  foos[i].classList.add('bar');
}

data- & dataset

All Browsers love getAttribute()

Dataset supported since IE10

Issue solved? Attribute Abuse

Attribute Abuse

<span 
  class="link externallink" 
  evt="5" 
  omnit="RFAW Review" 
  rel="www~trekaroo~com|activities|arizona-museum-of-natural-history-mesa-arizona" 
  target="_blank">
  Trekaroo
</span>

Author-defined attributes!

Add attributes to elements in the form of data-name and access these through the DOM using dataset[name] on the element in question.

<p data-foo-bar="test">
  That's an author defined attribute
</p>

dataset property returns a DOMStringMap object of element's data- attributes

el.dataset['fooBar']

data-* attributes

Valid code

<span 
  class="link"
  data-usage="externallink" 
  data-evt="5" 
  data-omnit="RFAW Review" 
  data-link="www.trekaroo.com/activities/arizona-museum-of-natural-history-mesa-arizona" 
  data-target="_blank">
  Trekaroo
</span>

Example

links=d.querySelectorAll('[data-usage]');

for (var i=0; i<links.length; i++) {
  switch(links[i].dataset.dataUsage){
    case "externallink":
      uptake.createExternalLink(links[i]);
      break;
    case "internallink":
      uptake.createInternalLink(links[i]);
      break;
    case "downloadlink":
      uptake.createDownloadLink(links[i]);
      break;
  }
}

dataset

dataset is a convenience feature for handling the data-* attributes, which are exposed as camel-cased properties.

e.dataset.fooBar = 'test'
e.dataset.usage = 'externallink' 

sets the data-foo-bar or data-usage attribute on e.

d.body.dataset.aBC = 'frog';

alert(d.body.getAttribute('data-a-b-c'));
alert(d.body.dataset['aBC']);
alert(d.body.dataset.aBC);

data-* attributes

Define your own DOM manipulable data attributes

<b class="card" data-value="10" 
data-suite="3" data-row="3" data-col="2"></b>
var cards = document.querySelectorAll('.card'),
            cardinfo;
            deck=[];
for(var i=0; i < cards.length; i++){
  cardinfo = [];
  for (var key in cards[i].dataset) {
    cardinfo.push(key,': ',cards[i].dataset[key],',');
  }
  var currentCard = cardinfo.join('');
  deck.push(currentCard);
}

(cont...)

currentCard == 'value: 10, suite: 3, row: 3, col: 2'
var cardID = info.getAttribute('data-value'); //10

All browsers support "data-*", but full support of 'dataset started with IE10, FF6, Safari 5.1, Chrome 7, Opera 11.1. Supported in iOS and Android. Polly fill

Notes on data- & dataset

  • Intended for site scripts, not publicly-usable metadata.
  • data-name is case insensitive (html), as it converts to lowercase (xhtml)
  • In JS, data-name is camelCased. Drop the "data-"
  • All browsers can use data-* attributes, accessing via getAttribute.
  • All browsers EXCEPT IE support accessing values with dataset property.

WHAT-WG Specification

When it comes to mobile...

  • 99% of mobile visits support addEventListener
  • Feature detection is the issue, not code parity
  • Memory, latency, CPU usage are major concerns.
  • Again: JS drains batteries!
  • Weigh the pros and cons of including a library... may not be worth it.

in General...

  • If you can do it in a few lines of code, don't import a library
  • If you can make the home page work without requiring the library, do it!
  • Don't import library as part of 3rd party widgets.
  • Use libraries as tools, not as a crutch.
  • Learn to code JavaScript not just jQuery.
  • Remember mobile