JavaScript: Do you really need a framework for that?

estelle.github.com/development/js.html

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

Library authors know what they are doing

  • Faster, optimized, readable code
  • Function chaining & implicit iteration behavior
  • Less code. Faster to code. Readable code.
  • Faster learning curve. No need to know JS?!?!

Seriously?

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

Using a library

Go

No library

Go

With jQuery

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

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

with Vanilla.js

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

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

No jquery

0KB extra + No HTTP request + 0J + No DNS lookup

Is it possible with only CSS?

Might work one day!

a[href^='http']:before {
  content: 'http://g.etfv.co/' + attr(href);
}
http://jsfiddle.net/a49ah7vg/2/

Mobile Matters

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

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.

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.

Natively...

vanilla js

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

or simply

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

Hit 4 for taking notes

Zebra Tables

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
var tr = d.getElementsByTagName('tr');
for(i=0; i < tr.length; i+=2) {
	tr[i].classList.add('even');
}
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;
}

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

Full Browser Support

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

JavaScript

getElementsByClassName()

Supported since IE9

getElementsByClassName()

on the document

document.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('A. ' + slides.length);

var foo = document.createElement('div');
foo.classList.add('slide');
container.appendChild(foo);
console.log('B. ' + slides.length);

Live Node List

  • Live NodeList
  • Case-sensitive
  • Empty NodeList returns true


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.

element.querySelector(selector);
element.querySelectorAll(selector);

Examples

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

Spec: Selectors API level 1 | Selectors API Level 2

static v. livenodeList

var gEBCN = 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 gEBCN: ' + gEBCN.length);
console.log('Current QSA: ' + QSA.length);

if you increase # of slides, getElementsByClassName (live) increases, querySelectorsAll (static) does not.

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

In the spec....
      but not yet supported

var match = el.matches(relative selector);  // false

var oneElement = el.find(relative selector);  // null
var nodeList = el.findAll(relative selector); // empty collection
Selectors API

classList

IE10+

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');
var fooCL = d.querySelector('#foo').classList;
    fooCL.add('bar');
    fooCL.remove('bar');
    fooCL.toggle('bar');
    fooCL.contains('bar'); // true or false

classList

foo = d.querySelector('#foo');
foo.classList.add('zap');
foo.classList.remove('bar');
foo.classList.toggle('bar');
foo.classList.contains('bar')); // true or false
var classes = foo.classList.toString();

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

Assignment

This slide, like all slides, has class 'slide'. The current slide also has class current. It is the only slide with that class. Some slides also have a class of 'intro'.

  1. Access the current slide.
  2. Add and remove the 'intro' class
  3. How many slides are there now, in all
  4. How many slides don't have the 'distant-slide' class?

data- & dataset

IE10+, but all browsers love getAttribute()

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"
  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']
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"
  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 set and get

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(''); // 'value: 10, suite: 3, row: 3, col: 2'
  deck.push(currentCard);
}

(cont...)

var cardID = info.getAttribute('data-value'); //10

or

var cardID = info.dataset.value; //10

All browsers support "data-*", but full support of 'dataset started with IE10, but if you need to support earlier IE, there's a 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 from IE10+ 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

Up Next: Elements