Adding/removing class with query results

Benchmark created by David Mark on


Description

Typical jQuery pattern is: find some elements, do something to them. As seen here, the process if painfully slow in jQuery.

Some will say you should learn an alternate, obscure jQuery $.* syntax for “optimization”. Then you lose all semblance of readability and the much-stressed “conciseness”. Why bother with it at that point?

If you start calling $.* methods, you are basically using something similar to the My Library API interface (that jQuery devotees claim to "hate"). Difference is you can't do most things that way in jQuery. And let's face it, that's not how most jQuery code is written. All about the "chaining", isn't it? :)

jQuery is meant for advertising agencies to create interactive mockups, not for writing production browser scripts. This is useful as advertising agencies don't normally employ JS programmers. I've yet to see a jQuery-based anything that was fit to put on a Website.

Learn to write efficient JS instead. It's far more readable and much better documented. Faster too; users will thank you, especially mobile. ;)

Preparation HTML

<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
/*
 * Context here is an HTML5 document
 * Appropriate build for this context would exclude XHTML support
 * Next line asserts document will create an HTML DOM
 * There are virtually no documents on the Web that create an XHTML DOM
 *
 */
var API = { disableXmlParseMode:true };
</script>
<script src="//www.cinsoft.net/mylib099-min.js"></script>
<script src="//www.cinsoft.net/mylib-qsa-min.js"></script>
<script>
  // For My Library test
  
  var forEach = API.forEach;
  var getEBCS = API.getEBCS
  
  // For My Library and cross-browser tests
  // Nothing special about these functions
  
  var addClass = API.addClass;
  var removeClass = API.removeClass;
  
  // For cross-browser test
  
  // Degrades below IE 5.5 (or anything else missing call method)
  // Remove else case to degrade in browsers lacking native forEach
  
  // NOTE: This forEach wrapper is *not* to be used with sparse arrays
  // Happily, queries produce no sparse arrays. :)
  
  var forEachElement, queryElements;
  
  if (Array.prototype.forEach) {
    forEachElement = function(elements, callback, thisObject) {
      elements.forEach(callback, thisObject);
    };
  } else if (Function.prototype.call) {
    forEachElement = function(elements, callback, thisObject) {
      for (var i = 0, l = elements.length; i < l; i++) {
        callback.call(thisObject || elements, elements[i], i, elements);
      }
    };
  }
  
  var toArray, canDoFastToArray;
  
  // NOTE: use isHostObjectProperty for feature detection
  
  if (document.childNodes) {
    try {
      Array.prototype.slice.call(document.childNodes, 0);
      canDoFastToArray = true;
    } catch(e) {
    }
  }
  
  // Remove else case to degrade in legacy IE (and many other old browsers)
  
  if (canDoFastToArray) {
    toArray = function(a) {
      return Array.prototype.slice.call(a, 0);
    };
  } else {
    toArray = function(a) {
      var result = [];
      for (var i = 0, l = a.length; i < l; i++) {
        result[i] = a[i];
      }
      return result;
    };
  }
  
  // Degrades below IE 8
  // Range of selectors is limited (and beyond scope of this test)
  // but better documented than jQuery's non-standard line-up
  // Best to stick to *simple* queries anyway.
  // Better yet, don't use queries ;)
  
  // NOTE: this wrapper is only for documents, in fact the one document. :)
  // Bothering with element-rooted queries is a waste and won't work the
  // way that current JS library users expect. Learn to expect the standard
  // behavior or stick to document-rooted queries.
  
  if (toArray && document.querySelectorAll) {
    queryElements = function(s) {
      return toArray(document.querySelectorAll(s));
    };
  }
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
My Library
forEach(getEBCS('label.inline'), function(el) {
  addClass(el, 'test');
  removeClass(el, 'test');
});
ready
My Library (OO)
Q(getEBCS('label.inline')).addClass('test').removeClass('test');

// Can also be written Q('label.inline')...
// but that will use internal query engine (not QSA provided by add-on)
ready
jQuery
jQuery('label.inline').addClass('test').removeClass('test');

// jQuery forces use of QSA and falls back to incompatible "Sizzle" engine
// for element-rooted queries, anything QSA doesn't like, etc.
// It's quite a confusing mishmash. :(
// Should learn QSA (Selectors API) way of doing things, not jQuery way.
ready
Cross-browser
// NOTE: API feature detection not normally in loop

if (forEachElement && queryElements && addClass) { // Conditionally defined
  forEachElement(queryElements('label.inline'), function(el) {
    addClass(el, 'test');
    removeClass(el, 'test');
  });
} else {
  throw new Error('Abort due to degraded browser');
}
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by David Mark on