My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Tuesday, May 20, 2014

134 bytes for an optimized and very basic jQuery like function

TL;DR this fits in a tweet :-)

What's New Here

The most tiny, common and probably used utility for quick prototyping is usually even smaller:
function $(S,P){return [].slice.call((P||document).querySelectorAll(S))}
but it's a performance killer for all cases where we use #selectors or even body since the engine inevitably needs to check all nodes in the DOM.

The Handy :first Pseudo Selector

Hard to believe W3C preferred two different but similar methods instead of simply adding a pseudo :first selector which is not standard and on top of it, it does not work as :first-child or any other selector ... the purpose would be to select only the first occurrence and stop the DOM parsing in an efficient way; instead we need to use querySelector, which is different from querySelectorAll.

A Consistent Result

Not only two different methods to use, we cannot operate in a similar way with those results anyway since one is collection like, where the length would be the indicator for empty results, while the singular version would be eventually null.

Well, Scratch That!

With desktop and mobile browsers released between 2008 and today we can do already many things using native APIs and here we have a probably not always perfect qSA that might fail using some crazy selector I personally try to avoid anyway, plus many Array extras that today are not extras anymore thanks to any sort of polyfill.
Good news is, we might want to polyfill only for jurassic browsers querySelector/All too ... so here what we can do:
// set all click actions per each link
$('a')
  .forEach(function (link) {
    link.addEventListener('click', this);
  },
  function click(e) {
    // prevent default
    e.preventDefault();
    // and show the link
    alert(e.currentTarget.href);
  }
);

// only first occurrence
$('.logout:first').forEach( ... action ... );

Going Wild Extending

The temptation to extend Array.prototype in order to simplify listeners handling is huge ... but while I am not telling you some library did it already, I'd like to show some trick to be less obtrusive and still super productive:
// simple utility to add an event listener
$.on = function (CSS, parentNode, type, handler, capture) {
  // in case parentNode is missing
  if (typeof type !== 'string') {
    capture = handler;
    handler = type;
    type = parentNode;
    parentNode = null;
  }
  return $(CSS, parentNode).map(function (el) {
    el.addEventListener(type, handler, capture);
    return el;
  });
};

// example
$.on('a', 'click', function (e) {
  // prevent default
  e.preventDefault();
  // and show the link
  alert(e.currentTarget.href);
});

That's Pretty Much It

We have a tiny, yet performant utility, and many ways to make it better for what we need on a wide range of already supported browsers.

No comments: