native JS vs Zepto (webkit only) (v3)

Revision 3 of this benchmark created on


Description

getElementById/getElementsByClassName vs querySelector/querySelectorAll vs Zepto vs jQuery

Preparation HTML

<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js">
</script>
<div id="native1">
  NATIVE 1 ID
</div>
<div class="native1">
  NATIVE 1 CLASS
</div>
<div class="native1">
  NATIVE 1 CLASS
</div>
<div class="native1">
  NATIVE 1 CLASS
</div>
<div class="native1">
  NATIVE 1 CLASS
</div>
<div class="native1">
  NATIVE 1 CLASS
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div id="native2">
  NATIVE 2 ID
</div>
<div class="native2">
  NATIVE 2 CLASS
</div>
<div class="native2">
  NATIVE 2 CLASS
</div>
<div class="native2">
  NATIVE 2 CLASS
</div>
<div class="native2">
  NATIVE 2 CLASS
</div>
<div class="native2">
  NATIVE 2 CLASS
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div id="zepto">
  ZEPTO ID
</div>
<div class="zepto">
  ZEPTO CLASS
</div>
<div class="zepto">
  ZEPTO CLASS
</div>
<div class="zepto">
  ZEPTO CLASS
</div>
<div class="zepto">
  ZEPTO CLASS
</div>
<div class="zepto">
  ZEPTO CLASS
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div id="jquery">
  JQUERY ID
</div>
<div class="jquery">
  JQUERY CLASS
</div>
<div class="jquery">
  JQUERY CLASS
</div>
<div class="jquery">
  JQUERY CLASS
</div>
<div class="jquery">
  JQUERY CLASS
</div>
<div class="jquery">
  JQUERY CLASS
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<script>
  //     Zepto.js
  //     (c) 2010, 2011 Thomas Fuchs
  //     Zepto.js may be freely distributed under the MIT license.
  var Zepto = (function() {
    var undefined, key, $$, classList, emptyArray = [],
        slice = emptyArray.slice,
        document = window.document,
        elementDisplay = {},
        classCache = {},
        getComputedStyle = document.defaultView.getComputedStyle,
        cssNumber = {
        'column-count': 1,
        'columns': 1,
        'font-weight': 1,
        'line-height': 1,
        'opacity': 1,
        'z-index': 1,
        'zoom': 1
        },
        fragmentRE = /^\s*<(\w+)[^>]*>/,
        elementTypes = [1, 9, 11],
        adjacencyOperators = ['after', 'prepend', 'before', 'append'],
        table = document.createElement('table'),
        tableRow = document.createElement('tr'),
        containers = {
        'tr': document.createElement('tbody'),
        'tbody': table,
        'thead': table,
        'tfoot': table,
        'td': tableRow,
        'th': tableRow,
        '*': document.createElement('div')
        },
        readyRE = /complete|loaded|interactive/,
        classSelectorRE = /^\.([\w-]+)$/,
        idSelectorRE = /^#([\w-]+)$/,
        tagSelectorRE = /^[\w-]+$/;

    function isF(value) {
      return ({}).toString.call(value) == "[object Function]"
    }

    function isO(value) {
      return value instanceof Object
    }

    function isA(value) {
      return value instanceof Array
    }

    function likeArray(obj) {
      return typeof obj.length == 'number'
    }

    function compact(array) {
      return array.filter(function(item) {
        return item !== undefined && item !== null
      })
    }

    function flatten(array) {
      return array.length > 0 ? [].concat.apply([], array) : array
    }

    function camelize(str) {
      return str.replace(/-+(.)?/g, function(match, chr) {
        return chr ? chr.toUpperCase() : ''
      })
    }

    function dasherize(str) {
      return str.replace(/::/g, '/').replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2').replace(/([a-z\d])([A-Z])/g, '$1_$2').replace(/_/g, '-').toLowerCase();
    }

    function uniq(array) {
      return array.filter(function(item, index, array) {
        return array.indexOf(item) == index
      })
    }

    function classRE(name) {
      return name in classCache ? classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'));
    }

    function maybeAddPx(name, value) {
      return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value;
    }

    function defaultDisplay(nodeName) {
      var element, display;
      if (!elementDisplay[nodeName]) {
        element = document.createElement(nodeName);
        document.body.appendChild(element);
        display = getComputedStyle(element, '').getPropertyValue("display");
        element.parentNode.removeChild(element);
        display == "none" && (display = "block");
        elementDisplay[nodeName] = display;
      }
      return elementDisplay[nodeName];
    }

    function fragment(html, name) {
      if (name === undefined) name = fragmentRE.test(html) && RegExp.$1;
      if (!(name in containers)) name = '*';
      var container = containers[name];
      container.innerHTML = '' + html;
      return slice.call(container.childNodes);
    }

    function Z(dom, selector) {
      dom = dom || emptyArray;
      dom.__proto__ = Z.prototype;
      dom.selector = selector || '';
      return dom;
    }

    function $(selector, context) {
      if (!selector) return Z();
      if (context !== undefined) return $(context).find(selector);
      else if (isF(selector)) return $(document).ready(selector);
      else if (selector instanceof Z) return selector;
      else {
        var dom;
        if (isA(selector)) dom = compact(selector);
        else if (elementTypes.indexOf(selector.nodeType) >= 0 || selector === window) dom = [selector], selector = null;
        else if (fragmentRE.test(selector)) dom = fragment(selector.trim(), RegExp.$1), selector = null;
        else if (selector.nodeType && selector.nodeType == 3) dom = [selector];
        else dom = $$(document, selector);
        return Z(dom, selector);
      }
    }

    $.extend = function(target) {
      slice.call(arguments, 1).forEach(function(source) {
        for (key in source) target[key] = source[key];
      })
      return target;
    }

    $.qsa = $$ = function(element, selector) {
      var found;
      return (element === document && idSelectorRE.test(selector)) ? ((found = element.getElementById(RegExp.$1)) ? [found] : emptyArray) : slice.call(
      classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) : tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) : element.querySelectorAll(selector));
    }

    function filtered(nodes, selector) {
      return selector === undefined ? $(nodes) : $(nodes).filter(selector);
    }

    function funcArg(context, arg, idx, payload) {
      return isF(arg) ? arg.call(context, idx, payload) : arg;
    }

    $.isFunction = isF;
    $.isObject = isO;
    $.isArray = isA;

    $.map = function(elements, callback) {
      var value, values = [],
          i, key;
      if (likeArray(elements)) for (i = 0; i < elements.length; i++) {
        value = callback(elements[i], i);
        if (value != null) values.push(value);
      } else for (key in elements) {
        value = callback(elements[key], key);
        if (value != null) values.push(value);
      }
      return flatten(values);
    }

    $.each = function(elements, callback) {
      var i, key;
      if (likeArray(elements)) for (i = 0; i < elements.length; i++) {
        if (callback.call(elements[i], i, elements[i]) === false) return elements;
      } else for (key in elements) {
        if (callback.call(elements[key], key, elements[key]) === false) return elements;
      }
      return elements;
    }

    $.fn = {
      forEach: emptyArray.forEach,
      reduce: emptyArray.reduce,
      push: emptyArray.push,
      indexOf: emptyArray.indexOf,
      concat: emptyArray.concat,
      map: function(fn) {
        return $.map(this, function(el, i) {
          return fn.call(el, i, el)
        });
      },
      slice: function() {
        return $(slice.apply(this, arguments));
      },
      ready: function(callback) {
        if (readyRE.test(document.readyState)) callback($);
        else document.addEventListener('DOMContentLoaded', function() {
          callback($)
        }, false);
        return this;
      },
      get: function(idx) {
        return idx === undefined ? this : this[idx]
      },
      size: function() {
        return this.length
      },
      remove: function() {
        return this.each(function() {
          if (this.parentNode != null) {
            this.parentNode.removeChild(this);
          }
        });
      },
      each: function(callback) {
        this.forEach(function(el, idx) {
          callback.call(el, idx, el)
        });
        return this;
      },
      filter: function(selector) {
        return $([].filter.call(this, function(element) {
          return element.parentNode && $$(element.parentNode, selector).indexOf(element) >= 0;
        }));
      },
      end: function() {
        return this.prevObject || $();
      },
      andSelf: function() {
        return this.add(this.prevObject || $())
      },
      add: function(selector, context) {
        return $(uniq(this.concat($(selector, context))));
      },
      is: function(selector) {
        return this.length > 0 && $(this[0]).filter(selector).length > 0;
      },
      not: function(selector) {
        var nodes = [];
        if (isF(selector) && selector.call !== undefined) this.each(function(idx) {
          if (!selector.call(this, idx)) nodes.push(this);
        });
        else {
          var excludes = typeof selector == 'string' ? this.filter(selector) : (likeArray(selector) && isF(selector.item)) ? slice.call(selector) : $(selector);
          this.forEach(function(el) {
            if (excludes.indexOf(el) < 0) nodes.push(el);
          });
        }
        return $(nodes);
      },
      eq: function(idx) {
        return idx === -1 ? this.slice(idx) : this.slice(idx, +idx + 1);
      },
      first: function() {
        var el = this[0];
        return el && !isO(el) ? el : $(el)
      },
      last: function() {
        var el = this[this.length - 1];
        return el && !isO(el) ? el : $(el)
      },
      find: function(selector) {
        var result;
        if (this.length == 1) result = $$(this[0], selector);
        else result = this.map(function() {
          return $$(this, selector)
        });
        return $(result);
      },
      closest: function(selector, context) {
        var node = this[0],
            candidates = $$(context || document, selector);
        if (!candidates.length) node = null;
        while (node && candidates.indexOf(node) < 0)
        node = node !== context && node !== document && node.parentNode;
        return $(node);
      },
      parents: function(selector) {
        var ancestors = [],
            nodes = this;
        while (nodes.length > 0)
        nodes = $.map(nodes, function(node) {
          if ((node = node.parentNode) && node !== document && ancestors.indexOf(node) < 0) {
            ancestors.push(node);
            return node;
          }
        });
        return filtered(ancestors, selector);
      },
      parent: function(selector) {
        return filtered(uniq(this.pluck('parentNode')), selector);
      },
      children: function(selector) {
        return filtered(this.map(function() {
          return slice.call(this.children)
        }), selector);
      },
      siblings: function(selector) {
        return filtered(this.map(function(i, el) {
          return slice.call(el.parentNode.children).filter(function(child) {
            return child !== el
          });
        }), selector);
      },
      empty: function() {
        return this.each(function() {
          this.innerHTML = ''
        })
      },
      pluck: function(property) {
        return this.map(function() {
          return this[property]
        })
      },
      show: function() {
        return this.each(function() {
          this.style.display == "none" && (this.style.display = null);
          if (getComputedStyle(this, '').getPropertyValue("display") == "none") {
            this.style.display = defaultDisplay(this.nodeName)
          }
        })
      },
      replaceWith: function(newContent) {
        return this.each(function() {
          $(this).before(newContent).remove();
        });
      },
      wrap: function(newContent) {
        return this.each(function() {
          $(this).wrapAll($(newContent)[0].cloneNode(false));
        });
      },
      wrapAll: function(newContent) {
        if (this[0]) {
          $(this[0]).before(newContent = $(newContent));
          newContent.append(this);
        }
        return this;
      },
      unwrap: function() {
        this.parent().each(function() {
          $(this).replaceWith($(this).children());
        });
        return this;
      },
      hide: function() {
        return this.css("display", "none")
      },
      toggle: function(setting) {
        return (setting === undefined ? this.css("display") == "none" : setting) ? this.show() : this.hide();
      },
      prev: function() {
        return $(this.pluck('previousElementSibling'))
      },
      next: function() {
        return $(this.pluck('nextElementSibling'))
      },
      html: function(html) {
        return html === undefined ? (this.length > 0 ? this[0].innerHTML : null) : this.each(function(idx) {
          var originHtml = this.innerHTML;
          $(this).empty().append(funcArg(this, html, idx, originHtml));
        });
      },
      text: function(text) {
        return text === undefined ? (this.length > 0 ? this[0].textContent : null) : this.each(function() {
          this.textContent = text
        });
      },
      attr: function(name, value) {
        var res;
        return (typeof name == 'string' && value === undefined) ? (this.length == 0 ? undefined : (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() : (!(res = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : res) : this.each(function(idx) {
          if (isO(name)) for (key in name) this.setAttribute(key, name[key])
          else this.setAttribute(name, funcArg(this, value, idx, this.getAttribute(name)));
        });
      },
      removeAttr: function(name) {
        return this.each(function() {
          this.removeAttribute(name);
        });
      },
      data: function(name, value) {
        return this.attr('data-' + name, value);
      },
      val: function(value) {
        return (value === undefined) ? (this.length > 0 ? this[0].value : null) : this.each(function(idx) {
          this.value = funcArg(this, value, idx, this.value);
        });
      },
      offset: function() {
        if (this.length == 0) return null;
        var obj = this[0].getBoundingClientRect();
        return {
          left: obj.left + window.pageXOffset,
          top: obj.top + window.pageYOffset,
          width: obj.width,
          height: obj.height
        };
      },
      css: function(property, value) {
        if (value === undefined && typeof property == 'string') {
          return (
          this.length == 0 ? undefined : this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property));
        }
        var css = '';
        for (key in property) css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';';
        if (typeof property == 'string') css = dasherize(property) + ":" + maybeAddPx(property, value);
        return this.each(function() {
          this.style.cssText += ';' + css
        });
      },
      index: function(element) {
        return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]);
      },
      hasClass: function(name) {
        if (this.length < 1) return false;
        else return classRE(name).test(this[0].className);
      },
      addClass: function(name) {
        return this.each(function(idx) {
          classList = [];
          var cls = this.className,
              newName = funcArg(this, name, idx, cls);
          newName.split(/\s+/g).forEach(function(klass) {
            if (!$(this).hasClass(klass)) {
              classList.push(klass)
            }
          }, this);
          classList.length && (this.className += (cls ? " " : "") + classList.join(" "))
        });
      },
      removeClass: function(name) {
        return this.each(function(idx) {
          if (name === undefined) return this.className = '';
          classList = this.className;
          funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass) {
            classList = classList.replace(classRE(klass), " ")
          });
          this.className = classList.trim()
        });
      },
      toggleClass: function(name, when) {
        return this.each(function(idx) {
          var newName = funcArg(this, name, idx, this.className);
          (when === undefined ? !$(this).hasClass(newName) : when) ? $(this).addClass(newName) : $(this).removeClass(newName);
        });
      }
    };

    'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property) {
      var fn = $.fn[property];
      $.fn[property] = function() {
        var ret = fn.apply(this, arguments);
        ret.prevObject = this;
        return ret;
      }
    });

    ['width', 'height'].forEach(function(dimension) {
      $.fn[dimension] = function(value) {
        var offset, Dimension = dimension.replace(/./, function(m) {
          return m[0].toUpperCase()
        });
        if (value === undefined) return this[0] == window ? window['inner' + Dimension] : this[0] == document ? document.documentElement['offset' + Dimension] : (offset = this.offset()) && offset[dimension];
        else return this.each(function(idx) {
          var el = $(this);
          el.css(dimension, funcArg(this, value, idx, el[dimension]()));
        });
      }
    });

    function insert(operator, target, node) {
      var parent = (operator % 2) ? target : target.parentNode;
      parent && parent.insertBefore(node, !operator ? target.nextSibling : // after
      operator == 1 ? parent.firstChild : // prepend
      operator == 2 ? target : // before
      null); // append
    }

    function traverseNode(node, fun) {
      fun(node);
      for (var key in node.childNodes) {
        traverseNode(node.childNodes[key], fun);
      }
    }

    adjacencyOperators.forEach(function(key, operator) {
      $.fn[key] = function(html) {
        var nodes = isO(html) ? html : fragment(html);
        if (!('length' in nodes) || nodes.nodeType) nodes = [nodes];
        if (nodes.length < 1) return this;
        var size = this.length,
            copyByClone = size > 1,
            inReverse = operator < 2;

        return this.each(function(index, target) {
          for (var i = 0; i < nodes.length; i++) {
            var node = nodes[inReverse ? nodes.length - i - 1 : i];
            traverseNode(node, function(node) {
              if (node.nodeName != null && node.nodeName.toUpperCase() === 'SCRIPT' && (!node.type || node.type === 'text/javascript')) {
                window['eval'].call(window, node.innerHTML);
              }
            });
            if (copyByClone && index < size - 1) node = node.cloneNode(true);
            insert(operator, target, node);
          }
        });
      };

      var reverseKey = (operator % 2) ? key + 'To' : 'insert' + (operator ? 'Before' : 'After');
      $.fn[reverseKey] = function(html) {
        $(html)[key](this);
        return this;
      };
    });

    Z.prototype = $.fn;

    return $;
  })();

  window.Zepto = Zepto;
  '$' in window || (window.$ = Zepto);
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
getElementById
processOne(document.getElementById('native1'));
ready
getElementsByClassName
processAll(document.getElementsByClassName('native1'));
ready
querySelector
processOne(document.querySelector('#native2'));
ready
querySelectorAll
processAll(document.querySelectorAll('.native2'));
ready
Zepto - ID selector
element = Zepto('#zepto');

element.css('background-color', randomColor()).html(content).bind('click', clickHandler);
ready
Zepto - class selector
elements = Zepto('.zepto');

elements.each(function() {
  Zepto(this).css('background-color', randomColor()).html(content).bind('click', clickHandler);
});
ready
jQuery - ID selector
element = jQuery('#jquery');

element.css('background-color', randomColor()).html(content).bind('click', clickHandler);
ready
jQuery - class selector
elements = jQuery('.jquery');

elements.each(function() {
  jQuery(this).css('background-color', randomColor()).html(content).bind('click', clickHandler);
});
ready

Revisions

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

  • Revision 1: published by Jakub on
  • Revision 2: published by Federico "Lox" Lucignano on
  • Revision 3: published on