beyond the final frontier of jquery selectors

61
Beyond the Final Frontier of jQuery Selectors Alexander Shopov <[email protected]>

Upload: alexander-shopov

Post on 10-May-2015

1.017 views

Category:

Technology


1 download

DESCRIPTION

How to extend jQuery, Sizzle selector engine

TRANSCRIPT

Page 1: Beyond the Final Frontier of jQuery Selectors

Beyond the Final Frontier of jQuery

SelectorsAlexander Shopov

<[email protected]>

Page 2: Beyond the Final Frontier of jQuery Selectors

By day: Software Engineer at CiscoBy night: OSS contributorCoordinator of Bulgarian Gnome TP Contacts:

E-mail: [email protected] Jabber: [email protected] LinkedIn: http://www.linkedin.com/in/alshopov SlideShare: http://www.slideshare.net/al_shopovWeb: Just search “al_shopov”

[ash@edge ~]$ whoami

Page 3: Beyond the Final Frontier of jQuery Selectors

Please Learn And Share

License: CC-BY v4.0

Page 4: Beyond the Final Frontier of jQuery Selectors

Contents

● The Bold And the Beautiful● Guiding Light● As The World Turns

Page 5: Beyond the Final Frontier of jQuery Selectors

The Bold And the Beautiful

Page 6: Beyond the Final Frontier of jQuery Selectors

Bold!

<style>.bold {font-weight: bold}</style><div id="d" class="bold">This is bold</div><script>jQuery(function (){ console.log(jQuery('#d').css('font-weight'));});</script>

Page 7: Beyond the Final Frontier of jQuery Selectors

Bold!!

<style>.bold {font-weight: bold}</style><div id="d" class="bold">This is bold</div><script>jQuery(function (){ console.log(jQuery('#d').css('font-weight'));});</script>

Chrome/Safari/Opera bold

Page 8: Beyond the Final Frontier of jQuery Selectors

Bold??

<style>.bold {font-weight: bold}</style><div id="d" class="bold">This is bold</div><script>jQuery(function (){ console.log(jQuery('#d').css('font-weight'));});</script>

Chrome/Safari/Opera bold

IE11/Firefox700

Page 9: Beyond the Final Frontier of jQuery Selectors

Thus Spoke The Standards

Value: normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900Initial: normalApplies to: all elementsInherited: yesPercentage values: N/ACSS1 [CSS Level 1/CSS Level 2 (Revision 1)/CSS Transitions/CSS Fonts Module Level 3]

100 - Thin 200 - Extra Light (Ultra Light) 300 - Light 400 - Normal 500 - Medium 600 - Semi Bold (Demi Bold) 700 - Bold 800 - Extra Bold (Ultra Bold) 900 - Black (Heavy)

100 ≤ 200 ≤ 300 ≤ 400 ≤ 500 ≤ 600 ≤ 700 ≤ 800 ≤ 900

Page 10: Beyond the Final Frontier of jQuery Selectors

Beautiful

● There is difference between Italic and Oblique font shapes

● Do you know it?

Page 11: Beyond the Final Frontier of jQuery Selectors

Beautiful

● Italics are stylized cursive writings – as if we write by hand. They have different shapes than roman/regular

● Obliques look like slanted roman/regular – poor man's italics

Page 12: Beyond the Final Frontier of jQuery Selectors

The Three Graces – Roman/regular, Italic, Oblique

Page 13: Beyond the Final Frontier of jQuery Selectors

How Can We Use Them With A Single Button?

Page 14: Beyond the Final Frontier of jQuery Selectors

How Can We Use Them? Web Fonts!<style type="text/css">@font-face { font-family: lmr; src: url(lmrtest-regular.otf);}@font-face { font-family: lmr; font-style: italic; src: url(lmrtest-italic.otf);}@font-face { font-family: lmr; font-style: oblique; src: url(lmrtest-oblique.otf);}</style>

<div style="font-family:lmr"><p style="font-style:normal"> Roman</p><p style="font-style:italic"> Italic</p><p style="font-style:oblique"> Oblique</p></div>

Page 15: Beyond the Final Frontier of jQuery Selectors

How Can We Find The Bold And The Beautiful?

function isBold(el){ var w = jQuery(el).css('font-weight'); return ('bold' === w) || (400 < (+w));}

function isSlanted(el){ var s = jQuery(el).css('font-style'); return ('italic' === s) || ('oblique' === s) ;}

Page 16: Beyond the Final Frontier of jQuery Selectors

Sorry Mario, Our Princess is in Another Castle

● We did not find the elements● We can just check whether a particular element is what we are

interested in● So we use it like this and create functions time and again:

jQuery('selector …').filter(function(i){ return isBold(i);});

Page 17: Beyond the Final Frontier of jQuery Selectors

A Slight Improvement

function bold4jQ(){ var w = jQuery(this).css('font-weight'); return ('bold' === w) || (400 < (+w));}jQuery('selector …').filter(bold4jQ);

function slanted4jQ(){ var s = jQuery(this).css('font-style'); return ('italic' === s) || ('oblique' === s);}jQuery('selector …').filter(slanted4jQ);

Page 18: Beyond the Final Frontier of jQuery Selectors

(You Gotta) Fight For Your Right (to jQuery)

● Why do this imperatively and explicitly call time and agang the same functions?

● jQuery has its own selector engine, why not use it to accomplish our needs

● Let's be declarative – which is more readable?

jQuery('selector …').filter(bold4jQ);jQuery('selector …').filter(slanted4jQ);

jQuery('selector:bold');jQuery('selector:slanted');

Page 19: Beyond the Final Frontier of jQuery Selectors

Guiding Light

Page 20: Beyond the Final Frontier of jQuery Selectors

This Is How We Do ItjQuery.extend(jQuery.expr[':'], { bold: function(el) { var w = jQuery(el).css('font-weight'); return ('bold' === w) || (400 < (+w)); }});jQuery('div:bold');

jQuery.extend(jQuery.expr[':'], { slanted: function(el) { var s = jQuery(el).css('font-style'); return ('italic' === s) || ('oblique' === s); }});jQuery('#editor span.usr:slanted');

Page 21: Beyond the Final Frontier of jQuery Selectors

Homework Asignments

● Find external links● Find links that lead to ASPX or JSP pages directly● Find divs with sizes above 42px● Find spans that are block elements or divs that are inline● The sky is the limit

Page 22: Beyond the Final Frontier of jQuery Selectors

Is This All There Is?

● We are not limited to no args functions. The function we provide is called with 3 arguments. We can dispatch behaviour based on all of them

/* returns boolean - whether given DOM * * element matches */matcher( /* single DOMElement from currenlty * * matched set to check */ DOMElement, /* index of the current element in * * in the matched set */ index, /* array of the split matching * * sequence */ matches);

Page 23: Beyond the Final Frontier of jQuery Selectors

Positional Parameters Example Usage

jQuery('selector…:font(14,400,italic)');

m = ['font', 'font', '', '14,400,italic'];

Page 24: Beyond the Final Frontier of jQuery Selectors

Positional Parameters Example Usage

jQuery('selector…:font(14,400,italic)');

m = ['font', 'font', '', '14,400,italic'];

m[3] === '14,400,italic';

Page 25: Beyond the Final Frontier of jQuery Selectors

Positional Parameters Example Usage

jQuery('selector…:font(14,400,italic)');

m = ['font', 'font', '', '14,400,italic'];

m[3] === '14,400,italic'; // split & coerce

m[3] === '14,400,italic';

Page 26: Beyond the Final Frontier of jQuery Selectors

Positional Parameters Example Usage

jQuery.extend(jQuery.expr[':'], { font: function(el, i, m){ var a = m[3].split(','), // dispatch l = a.length, // dispatch j = jQuery(el), // cache p = ['size', 'weight', 'style'], z = 0; // index if(0 === l){return true;} if(3 < l){throw {name:'Funky', message:'Nuts?'};} // or return false and fail silently for(;z<l;++z){if(j.css('font-'+p[z])!=a[z]){return false;}} return true; }});

Page 27: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example Usage

jQuery('selector…:font' + '(size>14,400<=weight,italic!=style,family!=Sans)'

m = ['font', 'font', '', 'size>14,400<=weight,italic!=style,family!=Sans'

Page 28: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example Usage

jQuery('selector…:font' + '(size>14,400<=weight,italic!=style,family!=Sans)'

m[3] === // what to do, what to do? 'size>14,400<=weight,italic!=style,family!=Sans'

m = ['font', 'font', '', 'size>14,400<=weight,italic!=style,family!=Sans'

Page 29: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example Usage

'size>14,400<=weight,italic!=style,family!=Sans'

Page 30: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example Usage // split, split, juggle & check'size>14,400<=weight,italic!=style,family!=Sans'

Page 31: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example Usage // split, split, juggle & check'size>14,400<=weight,italic!=style,family!=Sans'

// split, juggle & check['size>14','400<=weight','italic!=style','family!=Sans']

Page 32: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example Usage // split, split, juggle & check'size>14,400<=weight,italic!=style,family!=Sans'

// split, juggle & check['size>14','400<=weight','italic!=style','family!=Sans']

[['size','>','14' ], // juggle & check ['400','<=','weight' ], ['italic','!=','style'], ['family','!=','Sans' ]]

Page 33: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example Usage // split, split, juggle & check'size>14,400<=weight,italic!=style,family!=Sans'

// split, juggle & check['size>14','400<=weight','italic!=style','family!=Sans']

[['size','>','14' ], // juggle & check ['400','<=','weight' ], ['italic','!=','style'], ['family','!=','Sans' ]]

[['family','!=','Sans' ], // check left as an ['size' ,'>' ,'14' ], // excercise for the ['style' ,'!=','italic' ], // reader ['weight','<=','400' ]]

Page 34: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example UsageGetting Jiggy With It

m[3] === // be creative 'size>14,400<=weight,italic!=style,family!=Sans'

'size>14,400<=weight,italic!=style,family!=Sans'

Page 35: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example UsageGetting Jiggy With It

m[3] === // be creative 'size>14,400<=weight,italic!=style,family!=Sans'

'size>14,400<=weight,italic!=style,family!=Sans'

'size>14,400<=weight,italic!=style,family!=Sans'. replace(/,/g,'&&') === ???

Page 36: Beyond the Final Frontier of jQuery Selectors

Named Parameters Example UsageGetting Jiggy With It

m[3] === // be creative 'size>14,400<=weight,italic!=style,family!=Sans'

'size>14,400<=weight,italic!=style,family!=Sans'

'size>14,400<=weight,italic!=style,family!=Sans'. replace(/,/g,'&&') === ???

"size>14&&400<=weight&&italic!=style&&family!=Sans"

Page 37: Beyond the Final Frontier of jQuery Selectors

Looks familiar, doesnt' it?

var expression ="size>14&&400<=weight&&italic!=style&&family!=Sans";

● But what are size, weight, style, family?● What about italic and Sans?

Page 38: Beyond the Final Frontier of jQuery Selectors

But We Already Know That

size: j.css('font-size') weight: j.css('font-weight') style: j.css('font-style') family: j.css('font-family') italic: 'italic', Sans: 'Sans'

Page 39: Beyond the Final Frontier of jQuery Selectors

Why Didn't You Say So?

var scope = { size: j.css('font-size'), weight: j.css('font-weight'), style: j.css('font-style'), family: j.css('font-family'), italic: 'italic', Sans: 'Sans'};

Page 40: Beyond the Final Frontier of jQuery Selectors

Mojo!!!

var r;with (scope){ r = eval (expression);}// PROFIT!!!

Page 41: Beyond the Final Frontier of jQuery Selectors

Now That You Know How

● DON'T!!!● Use to explore, you brave explorers.● Use in production is misuse – will void your warranty, kill kittens

and cause plague.

Page 42: Beyond the Final Frontier of jQuery Selectors

So What Is The Catch?

● Quick to implement, experiment, POC;● Piggyback on JavaScript as a host for the DSL;● Hard to debug, with weird error messages in strange places – still

we are in control of the scope template and this can be mitigated;● Blocks browser's JS optimization engine (but we are not the only

ones);● Jslint will complain – we are treading in one of the the darkest

places in the language.

Page 43: Beyond the Final Frontier of jQuery Selectors

How Do You Say De-Groovie?How Do You Say De-Bug?

jQuery('selector…:debug');

jQuery.extend(jQuery.expr[':'], { debug: function(el, i, m) { console.log("args: %o", arguments); console.log("m: %o", m); return true; }});

Page 44: Beyond the Final Frontier of jQuery Selectors

The Power Of Their Source? The Crystal!

define(["./core", "sizzle"], function( jQuery, Sizzle ) { jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.pseudos; jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains;});

https://github.com/jquery/jquery/blob/master/src/selector-sizzle.js

Page 45: Beyond the Final Frontier of jQuery Selectors

As the World Turns

Page 46: Beyond the Final Frontier of jQuery Selectors

Sizzle, Who the (BLEEP) is Sizzle?

● Sizzle: A pure-JavaScript CSS selector engine designed to be easily dropped in to a host library.

● Embedded in jQuery, MooTools, Dojo

Version Supported browsers LOC Minified API

jQuery 1.10.2 IE6+, FF(Current-1)+, Chrome(Current-1)+, Safari 5.1+, Opera 12.1x, (Current-1)+

≈10k ≈93kB Vast

jQuery 2.0.3 IE9+, FF(Current-1)+, Chrome(Current-1)+, Safari 5.1+, Opera 12.1x, (Current-1)+

≈9k ≈84kB Vast

Sizzle 1.10.1x IE6+, FF3.0+, Chrome 5+, Safari 3+, Opera 9+

≈2k ≈18kB Tiny

Page 47: Beyond the Final Frontier of jQuery Selectors

Sizzle API – part 1

/* returns boolean - whether given DOM * * element matches selector * * If available uses native matchesSelector */Sizzle.matchesSelector( /* DOMElement to check */ DOMElement, /* String – CSS Selector */ selector);

Page 48: Beyond the Final Frontier of jQuery Selectors

Sizzle API – part 2

/* returns Array of the elements that match * * the selector */Sizzle.matches( /* String – CSS Selector */ selector, /* Array of DOMElements */ elements);

Page 49: Beyond the Final Frontier of jQuery Selectors

Sizzle API – part 3/* Return nodes that match selector * [starting from the context node] * as an array [or added to the provided * array-like object] */Sizzle( /* String – CSS Selector */ selector, /* Optional: DOMElement|DOMDocument * * Default: Current document * * Node to start the search from */ context, /* Optional: Array-like object to * * add elements to * * eg.: jQuery() obj * * Default: Create new array */ results);

Page 50: Beyond the Final Frontier of jQuery Selectors

That's All Folks!

This was the whole public API!

Page 51: Beyond the Final Frontier of jQuery Selectors

Do It Very Slowly (True Lies)

● document.querySelectorAll is very fast and meddles with our meddling

● Get rid of it!

document.querySelectorAll = null; // or any falsy// why not?delete document.querySelectorAll;

Page 52: Beyond the Final Frontier of jQuery Selectors

Alien

● querySelectorAll (as well as querySelector) comes from elsewhere – from the prototype world

● Prototype world is wilder that the West!● Object.getPrototypeOf() – IE, Chrome, Firefox● {}.__proto__ – Firefox, Chome

Page 53: Beyond the Final Frontier of jQuery Selectors

Alien 2 – IE 10

document

DocumentProtoype

NodeProtoype

window

Window

Object

nullquerySelectorAllWORKS: = null;

FAILS: delete

Page 54: Beyond the Final Frontier of jQuery Selectors

Alien 4 – Firefox

document

HTMLDocumentPrototype

DocumentPrototype

NodePrototype

EventTarget

Prototype

window

[WindowPrototype]

Object

nullquerySelectorAll

WORKS: = null;FAILS: delete

Page 55: Beyond the Final Frontier of jQuery Selectors

Alien 3 – Chrome

document

HTMLDocument

Document

Node

EventTarget

window

Window

Object

nullquerySelectorAll

WORKS: = null;FAILS: delete

Page 56: Beyond the Final Frontier of jQuery Selectors

Alien 4 – Firefox

document

HTMLDocumentPrototype

DocumentPrototype

NodePrototype

EventTarget

Prototype

window

[WindowPrototype]

Object

nullquerySelectorAll

WORKS: = null;FAILS: delete

Page 57: Beyond the Final Frontier of jQuery Selectors

What We Used Thus Far

// Thus far we actually extendedSizzle.selectors.pseudos;

//jQuery does some magic to ease using Sizzle.selectors.createPseudo(function(){});

Page 58: Beyond the Final Frontier of jQuery Selectors

Where Next// Matches parts of selector (as string)// divides in partsSizzle.selectors.match.NAME = new RegExp('...');

Sizzle.selectors.find.NAME = function( // what was matched above match, // element to start search from context, // whether doc is XML/HTML IsXML ) {};

// regex of orders, add |NAME to itSizzle.selectors.order;

Page 59: Beyond the Final Frontier of jQuery Selectors

Where Next

// quick prefilter, eg: remove all attributes or // elements based on a quick decisionSizzle.selectors.preFilter.NAME = function( match ) {};// slow filter invoked after the one above (if )∃// real evaluationSizzle.selectors.filter.NAME = function(element, m1, m2, m3 ) {};

Page 60: Beyond the Final Frontier of jQuery Selectors

Sizzle is extensible

● No extension tests in test harness;● Dragons around the corers, YMMV;● Looooooooooooooooo + ooooooooooooo + oooo + oooooo +

oooooooo + oooooo + ooooooooooooooooooooooooooo + oooooooo + ooooooooooo + ooooong regular expressions;

● Actual regular expressions are longer.

Page 61: Beyond the Final Frontier of jQuery Selectors

TO BE IMPROVED...