Skip to content

Instantly share code, notes, and snippets.

Revisions

  1. @yorkxin yorkxin revised this gist Jul 30, 2012. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -84,6 +84,8 @@ element.classList.contains("hide");

    jQuery's [`.css()`](http://api.jquery.com/css/) is useful for style manipulation (merging one set into another set). But if you only have to assign one attribute or override the whole style rule, don't use it.

    Especially, if you care about performance, or doing lots of style manipulation, you should avoid `.css()` as possible as you can, because `.css` is really slow and will be your performance bottleneck. ([Experiment](http://jsperf.com/style-versus-jquery-css/8); [another experiment](http://jsperf.com/jquery-css-vs-dom-style) by me.)

    Alternatives:

    ### Assigning One Attribute
  2. @yorkxin yorkxin revised this gist Jul 20, 2012. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -149,6 +149,8 @@ element.dataset.catWeight; // => "100t"

    Note that the name of data will be converted to camelCase, see [the spec](http://dev.w3.org/html5/spec/global-attributes.html#embedding-custom-non-visible-data-with-the-data-attributes) for more details.

    ### Sometimes `.data()` is Useful

    The following scenarios shows when `.data()` is more useful than DataSet:

    ```javascript
  3. @yorkxin yorkxin revised this gist Jul 20, 2012. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -116,8 +116,6 @@ For Arrays, there is a native [`for` loop](https://developer.mozilla.org/en/Java

    However `for` statements does not provide **isolated variable scope** (i.e. process each item with a callback function), which `$.each()` does provide. Fortunately in ECMAScript 5 there is an [`array.forEach`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach) function that provides an isolated variable scope with a callback function ([polyfill](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach#Compatibility) or [ES5-Shim][es5-shim]), just like `$.each`. But so far there is no `object.forEach` available. In this case you may need `$.each`.

    Although `$.each()` doesn't often create performance issue

    ## `.data()` → use HTML5 `dataset` (for string)

    jQuery's [`.data()`](http://api.jquery.com/data/) can store various types of data within a given element. However it implements its own storage, which causes a lot of overhead if you're only storing Strings with it.
  4. @yorkxin yorkxin revised this gist Jul 20, 2012. 1 changed file with 21 additions and 4 deletions.
    25 changes: 21 additions & 4 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -196,13 +196,30 @@ $(element2).data("scaling"); // => function(n) {...}, a Function, as expected

    ### Support for Old Browsers

    If you need to support old browsers, take a look at [this polyfill](http://eligrey.com/blog/post/html-5-dataset-support), or use [`createAttribute`](https://developer.mozilla.org/en/DOM/document.createAttribute) and [`setAttributeNode`](https://developer.mozilla.org/en/DOM/element.setAttributeNode). However attribute node manipulation is [deprecated in DOM 4 spec](http://www.w3.org/TR/dom/#dom-core) (in favor of `setAttribute`?). If you care about the web standard, maybe polyfilling `dataset` is a better solution.
    If you need to support old browsers, take a look at [this polyfill](http://eligrey.com/blog/post/html-5-dataset-support), or use [`getAttribute`](https://developer.mozilla.org/en/DOM/element.getAttribute) and [`setAttribute`](https://developer.mozilla.org/en/DOM/element.setAttribute). For browsers without getAttribute and setAttribute support, e.g. IE 7, you need [`createAttribute`](https://developer.mozilla.org/en/DOM/document.createAttribute), [`getAttributeNode`](https://developer.mozilla.org/en/DOM/element.getAttributeNode) and [`setAttributeNode`](https://developer.mozilla.org/en/DOM/element.setAttributeNode), although they're [deprecated in DOM 4 spec](http://www.w3.org/TR/dom/#dom-core).

    Example:
    Example: Use `getAttribute` and `setAttribute`

    ```javascript
    /* Note: element.*AttributeNode and document.createAttribute are deprecated in DOM 4 */
    // use the same HTML structure above

    var element = document.getElementById("long-cat");

    // read the existing data:
    element.getAttribute("data-cat-length"); // => "30km"

    // write the existing data:
    element.setAttribute("data-cat-length", "42km");
    element.getAttribute("data-cat-length"); // => "42km"

    // write a new data dynamically:
    element.setAttribute("data-cat-weight", "100t");
    element.getAttributeNode("data-cat-weight"); // => "100t"
    ```

    Example: Use `createAttribute`, `getAttributeNode` and `setAttributeNode` (deprecated in DOM 4)

    ```javascript
    // use the same HTML structure above

    var element = document.getElementById("long-cat");
    @@ -242,4 +259,4 @@ Desktop WebKit browsers like Chrome and Safari, as well as Internet Explorer 9+,

    p.s. This is made public from my [private Gist](https://gist.github.com/034fe1d3b0a5c9952eba).

    [es5-shim]: https://github.com/kriskowal/es5-shim/ "ES5-Shim"
    [es5-shim]: https://github.com/kriskowal/es5-shim/ "ES5-Shim"
  5. @yorkxin yorkxin revised this gist Jul 20, 2012. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -114,7 +114,7 @@ For Arrays, there is a native [`for` loop](https://developer.mozilla.org/en/Java

    ### Isolated Variable Scope

    However `for` statements does not provide **isolated variable scope** (i.e. process each item with a callback function), which `$.each()` does provide. Fortunately in ECMAScript 5 there is an [`array.forEach`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach) function that provides an isolated variable scope with a callback function ([polyfill](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach#Compatibility)), just like `$.each`. But so far there is no `object.forEach` available. In this case you may need `$.each`.
    However `for` statements does not provide **isolated variable scope** (i.e. process each item with a callback function), which `$.each()` does provide. Fortunately in ECMAScript 5 there is an [`array.forEach`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach) function that provides an isolated variable scope with a callback function ([polyfill](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach#Compatibility) or [ES5-Shim][es5-shim]), just like `$.each`. But so far there is no `object.forEach` available. In this case you may need `$.each`.

    Although `$.each()` doesn't often create performance issue

    @@ -238,5 +238,8 @@ Desktop WebKit browsers like Chrome and Safari, as well as Internet Explorer 9+,
    - [HTML5 Cross Browser Polyfills · Modernizr/Modernizr Wiki](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills)
    - [WWDC 2012 Session 601](https://developer.apple.com/videos/wwdc/2012/?id=601) (I learnt `classList` and `querySelectorAll` in the video)
    - [ECMAScript 5 compatibility table](http://kangax.github.com/es5-compat-table/)
    - [ES5-Shim: ECMAScript 5 compatibility shims for legacy JavaScript engines][es5-shim]

    p.s. This is made public from my [private Gist](https://gist.github.com/034fe1d3b0a5c9952eba).
    p.s. This is made public from my [private Gist](https://gist.github.com/034fe1d3b0a5c9952eba).

    [es5-shim]: https://github.com/kriskowal/es5-shim/ "ES5-Shim"
  6. @yorkxin yorkxin revised this gist Jul 20, 2012. 1 changed file with 9 additions and 4 deletions.
    13 changes: 9 additions & 4 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -106,13 +106,17 @@ element.style.cssText = "width: 320px; height: 640px;"; // Mind the semicolon
    // instead of $(element).css({"width": "320px", "height": "640px"});
    ```

    ## `$.each()` → use `for` statement
    ## `$.each()` → use `for` statement or `.forEach` (ECMAScript 5)

    jQuery's [`$.each()`](http://api.jquery.com/jQuery.each/) is convenient when dealing with both `Array` and `Object` types, but why are we so lazy to know the actual data type?
    jQuery's [`$.each()`](http://api.jquery.com/jQuery.each/) is convenient when dealing with both `Array` and `Object` types, but we should already know the actual data type and process it properly.

    Although `$.each()` doesn't often create performance issue, we can use native `for` loop (for array) and `for..in` iterator (for object).
    For Arrays, there is a native [`for` loop](https://developer.mozilla.org/en/JavaScript/Reference/Statements/for); for Objects, there is a native [`for..in` iterator](https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in).

    The only benefit of `$.each` is that it uses a callback, so if you need an isolated variable scope, then `$.each` is better.
    ### Isolated Variable Scope

    However `for` statements does not provide **isolated variable scope** (i.e. process each item with a callback function), which `$.each()` does provide. Fortunately in ECMAScript 5 there is an [`array.forEach`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach) function that provides an isolated variable scope with a callback function ([polyfill](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach#Compatibility)), just like `$.each`. But so far there is no `object.forEach` available. In this case you may need `$.each`.

    Although `$.each()` doesn't often create performance issue

    ## `.data()` → use HTML5 `dataset` (for string)

    @@ -233,5 +237,6 @@ Desktop WebKit browsers like Chrome and Safari, as well as Internet Explorer 9+,
    - [Statements - JavaScript Reference - MDN](https://developer.mozilla.org/en/JavaScript/Reference#s26545)
    - [HTML5 Cross Browser Polyfills · Modernizr/Modernizr Wiki](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills)
    - [WWDC 2012 Session 601](https://developer.apple.com/videos/wwdc/2012/?id=601) (I learnt `classList` and `querySelectorAll` in the video)
    - [ECMAScript 5 compatibility table](http://kangax.github.com/es5-compat-table/)

    p.s. This is made public from my [private Gist](https://gist.github.com/034fe1d3b0a5c9952eba).
  7. @yorkxin yorkxin revised this gist Jul 20, 2012. 1 changed file with 48 additions and 3 deletions.
    51 changes: 48 additions & 3 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -114,11 +114,13 @@ Although `$.each()` doesn't often create performance issue, we can use native `f

    The only benefit of `$.each` is that it uses a callback, so if you need an isolated variable scope, then `$.each` is better.

    ## `.data()` → use HTML5 `dataset`
    ## `.data()` → use HTML5 `dataset` (for string)

    jQuery's [`.data()`](http://api.jquery.com/data/) implements its own storage, which may cause lots of overhead.
    jQuery's [`.data()`](http://api.jquery.com/data/) can store various types of data within a given element. However it implements its own storage, which causes a lot of overhead if you're only storing Strings with it.

    If you're only supporting browsers with native [HTML5 DataSet](http://caniuse.com/#feat=dataset) implementation, use [`element.dataset`](https://developer.mozilla.org/en/DOM/element.dataSet) and its corresponding API to access the bounded data. Also see HTML5 Doctor's [Guide on HTML5 data-* Attributes](http://html5doctor.com/html5-custom-data-attributes/).
    Modern browsers has [native HTML5 DataSet](http://caniuse.com/#feat=dataset) implementation. With [`element.dataset`](https://developer.mozilla.org/en/DOM/element.dataSet) and its corresponding APIs, you can access the data bounded by `data-*` attributes. Also see HTML5 Doctor's [Guide on HTML5 data-* Attributes](http://html5doctor.com/html5-custom-data-attributes/).

    The only limitation of HTML5 DataSet is that **it can only store String** type. `.data()` in jQuery can store any type of data, not only a nested Object or Array, but also a Function. If you do need to store types other than String, it is better to use `.data()`. It is also a better way to store a Number, because if you use DataSet you'll need to `parseInt` every time before you process the data.

    Say that we have the following HTML:

    @@ -145,6 +147,49 @@ element.dataset.catWeight; // => "100t"

    Note that the name of data will be converted to camelCase, see [the spec](http://dev.w3.org/html5/spec/global-attributes.html#embedding-custom-non-visible-data-with-the-data-attributes) for more details.

    The following scenarios shows when `.data()` is more useful than DataSet:

    ```javascript
    var element1 = document.createElement("div"),
    element2 = document.createElement("div");

    /* Storing a Number */

    element1.dataset.length = 42000;
    element1.dataset.length; // => "42000", a String, not a Number

    $(element2).data("length", 42000);
    $(element2).data("length"); // => 42000, a Number, as expected

    /* Storing an Object */

    element1.dataset.profile = { name: "Nyan", age: 3};
    element1.dataset.profile; // => [object Object], not what we want.

    $(element2).data("profile", { name: "Nyan", age: 3});
    $(element2).data("profile"); // => { name: "Nyan", age: 3}, an Object, as expected

    /* Storing an Array */

    element1.dataset.primes = [2, 3, 5, 7, 11, 13];
    element1.dataset.primes; // => "2,3,5,7,11,13", a String, not an Array

    $(element2).data("primes", [2, 3, 5, 7, 11, 13]);
    $(element2).data("primes"); // => [2, 3, 5, 7, 11, 13], an Array, as expected

    /* Storing a Function */

    var scaling = function(n) {
    return n * 3;
    };

    element1.dataset.scaling = scaling;
    element1.dataset.scaling; // => "function(n) {...}", a String representing the function literally, not a Function

    $(element2).data("scaling", scaling);
    $(element2).data("scaling"); // => function(n) {...}, a Function, as expected
    ```

    ### Support for Old Browsers

    If you need to support old browsers, take a look at [this polyfill](http://eligrey.com/blog/post/html-5-dataset-support), or use [`createAttribute`](https://developer.mozilla.org/en/DOM/document.createAttribute) and [`setAttributeNode`](https://developer.mozilla.org/en/DOM/element.setAttributeNode). However attribute node manipulation is [deprecated in DOM 4 spec](http://www.w3.org/TR/dom/#dom-core) (in favor of `setAttribute`?). If you care about the web standard, maybe polyfilling `dataset` is a better solution.
  8. @yorkxin yorkxin revised this gist Jul 20, 2012. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -152,6 +152,8 @@ If you need to support old browsers, take a look at [this polyfill](http://eligr
    Example:

    ```javascript
    /* Note: element.*AttributeNode and document.createAttribute are deprecated in DOM 4 */

    // use the same HTML structure above

    var element = document.getElementById("long-cat");
  9. @yorkxin yorkxin revised this gist Jul 20, 2012. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -147,7 +147,9 @@ Note that the name of data will be converted to camelCase, see [the spec](http:/

    ### Support for Old Browsers

    If you need to support old browsers, take a look at [this polyfill](http://eligrey.com/blog/post/html-5-dataset-support), or use [`createAttribute`](https://developer.mozilla.org/en/DOM/document.createAttribute) and [`setAttributeNode`](https://developer.mozilla.org/en/DOM/element.setAttributeNode):
    If you need to support old browsers, take a look at [this polyfill](http://eligrey.com/blog/post/html-5-dataset-support), or use [`createAttribute`](https://developer.mozilla.org/en/DOM/document.createAttribute) and [`setAttributeNode`](https://developer.mozilla.org/en/DOM/element.setAttributeNode). However attribute node manipulation is [deprecated in DOM 4 spec](http://www.w3.org/TR/dom/#dom-core) (in favor of `setAttribute`?). If you care about the web standard, maybe polyfilling `dataset` is a better solution.

    Example:

    ```javascript
    // use the same HTML structure above
  10. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -155,11 +155,11 @@ If you need to support old browsers, take a look at [this polyfill](http://eligr
    var element = document.getElementById("long-cat");

    // read the existing data:
    element.getAttributeNode("data-cat-length").nodeValue; // => "30km";
    element.getAttributeNode("data-cat-length").nodeValue; // => "30km"

    // write the existing data:
    element.getAttributeNode("data-cat-length").nodeValue = "42km";
    element.getAttributeNode("data-cat-length").nodeValue// => "42km";
    element.getAttributeNode("data-cat-length").nodeValue = "42km"
    element.getAttributeNode("data-cat-length").nodeValue; // => "42km"

    // create a new data dynamically:
    var attribute = document.createAttribute("data-cat-weight");
  11. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ When targeting only modern browsers, it is better to avoid using jQuery's backwa

    If you're making a web page for iOS (e.g. UIWebView), you should use native DOM APIs because mobile Safari is not that old-school web browser; it supports lots of native DOM APIs.

    If you're making a Chrome Extension, you should **always** use native APIs, not only because Chrome has almost the latest DOM APIs available, but this can also avoid performance issue and unnecessary memory occupation (each jQuery-driven extension needs a separate memory space for its jQuery's compiled machine code; they're not shared across extensions). One exceptions is that when you're making an isolated app-like extension, which works just like another tab, and no need to care about memory occupation.
    If you're making a Chrome Extension, you should **always** use native APIs, not only because Chrome has almost the latest DOM APIs available, but this can also avoid performance issue and unnecessary memory occupation (each jQuery-driven extension needs a separate memory space for its jQuery's compiled machine code; they're not shared across extensions). One exception is the app-like extension, which works just like another tab, and no need to care about memory occupation.

    ## `$("#header")``getElementById()`

  12. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ When targeting only modern browsers, it is better to avoid using jQuery's backwa

    If you're making a web page for iOS (e.g. UIWebView), you should use native DOM APIs because mobile Safari is not that old-school web browser; it supports lots of native DOM APIs.

    If you're making a Chrome Extension, you should **always** use native APIs, not only because Chrome has almost the latest DOM APIs available, but this can also avoid performance issue and unnecessary memory occupation unless you're making an app-like extension (each jQuery-driven extension needs a separate memory space for its jQuery's compiled machine code; they're not shared across extensions).
    If you're making a Chrome Extension, you should **always** use native APIs, not only because Chrome has almost the latest DOM APIs available, but this can also avoid performance issue and unnecessary memory occupation (each jQuery-driven extension needs a separate memory space for its jQuery's compiled machine code; they're not shared across extensions). One exceptions is that when you're making an isolated app-like extension, which works just like another tab, and no need to care about memory occupation.

    ## `$("#header")``getElementById()`

  13. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -171,7 +171,7 @@ element.getAttributeNode("data-cat-weight").nodeValue; // => "100t"

    ## How to Tell the Performance Improvement?

    Desktop WebKit browsers like Chrome and Safari, as well as Internet Explorer 9+, have built-in JavaScript CPU Profilers. Use them to trace function running time and decide the bottleneck of your javascript code:
    Desktop WebKit browsers like Chrome and Safari, as well as Internet Explorer 9+, have built-in JavaScript CPU Profilers. Use them to trace function running time and determine the bottleneck of your javascript code:

    - [Chrome Developer Tools: CPU Profiling](https://developers.google.com/chrome-developer-tools/docs/cpu-profiling)
    - [Safari Developer Tools Guide: Optimizing Your Website](https://developer.apple.com/library/safari/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/OptimizingYourWebsite/OptimizingYourWebsite.html#//apple_ref/doc/uid/TP40007874-CH9-SW1)
  14. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 9 additions and 0 deletions.
    9 changes: 9 additions & 0 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -169,6 +169,15 @@ element.setAttributeNode(attribute);
    element.getAttributeNode("data-cat-weight").nodeValue; // => "100t"
    ```

    ## How to Tell the Performance Improvement?

    Desktop WebKit browsers like Chrome and Safari, as well as Internet Explorer 9+, have built-in JavaScript CPU Profilers. Use them to trace function running time and decide the bottleneck of your javascript code:

    - [Chrome Developer Tools: CPU Profiling](https://developers.google.com/chrome-developer-tools/docs/cpu-profiling)
    - [Safari Developer Tools Guide: Optimizing Your Website](https://developer.apple.com/library/safari/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/OptimizingYourWebsite/OptimizingYourWebsite.html#//apple_ref/doc/uid/TP40007874-CH9-SW1)
    - [Internet Explorer - Using the Profiler Tool to analyze the performance of your code](http://msdn.microsoft.com/en-us/library/ie/gg699341.aspx) (also includes invocation counter)


    ## References

    1. [Gecko DOM Reference - MDN](https://developer.mozilla.org/en/Gecko_DOM_Reference)
  15. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ When targeting only modern browsers, it is better to avoid using jQuery's backwa

    If you're making a web page for iOS (e.g. UIWebView), you should use native DOM APIs because mobile Safari is not that old-school web browser; it supports lots of native DOM APIs.

    If you're making a Chrome Extension, you should **always** use native APIs, not only because Chrome has almost the latest DOM APIs available, but this can also avoid performance issue and unnecessary memory occupation (each extension needs a separate memory space for its jQuery; it is not shared).
    If you're making a Chrome Extension, you should **always** use native APIs, not only because Chrome has almost the latest DOM APIs available, but this can also avoid performance issue and unnecessary memory occupation unless you're making an app-like extension (each jQuery-driven extension needs a separate memory space for its jQuery's compiled machine code; they're not shared across extensions).

    ## `$("#header")``getElementById()`

  16. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -76,7 +76,7 @@ element.classList.toggle("hide");
    // instead of $(element).toggleClass("hide")

    // test if the element has some class
    element.contains("hide");
    element.classList.contains("hide");
    // instead of $(element).hasClass("hide")
    ```

  17. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -53,7 +53,7 @@ The [`element.className`](https://developer.mozilla.org/en/DOM/element.className

    jQuery provides some [class manipulation functions](http://api.jquery.com/category/manipulation/class-attribute/) such as `addClass()`, `removeClass()`, `toggleClass()` as well as `hasClass()`. With the new DOM API `classList`, we can avoid these functions.

    Unfortunately IE <= 9 does not support `classList`. In this case you can use polyfills like [classList.js](https://github.com/eligrey/classList.js) or [this polyfill for IE 9](https://gist.github.com/1381839), or implement it manually by tokenizing the `className` string into an array, just like [what jQuery does](https://github.com/jquery/jquery/blob/master/src/attributes.js#L36).
    Unfortunately IE <= 9 does not support `classList`. In this case you can use polyfills like [classList.js](https://github.com/eligrey/classList.js) or [this polyfill for IE 9](https://gist.github.com/1381839), or implement it manually by tokenizing the `className` string into an array like [how jQuery does](https://github.com/jquery/jquery/blob/master/src/attributes.js#L36), or by working with space-wrapped strings like [how MooTools does](https://github.com/mootools/mootools-core/blob/master/Source/Element/Element.js#L697).

    ### `classList` Usage

  18. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -174,4 +174,6 @@ element.getAttributeNode("data-cat-weight").nodeValue; // => "100t"
    1. [Gecko DOM Reference - MDN](https://developer.mozilla.org/en/Gecko_DOM_Reference)
    - [Statements - JavaScript Reference - MDN](https://developer.mozilla.org/en/JavaScript/Reference#s26545)
    - [HTML5 Cross Browser Polyfills · Modernizr/Modernizr Wiki](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills)
    - [WWDC 2012 Session 601](https://developer.apple.com/videos/wwdc/2012/?id=601) (I learnt `classList` and `querySelectorAll` in the video)
    - [WWDC 2012 Session 601](https://developer.apple.com/videos/wwdc/2012/?id=601) (I learnt `classList` and `querySelectorAll` in the video)

    p.s. This is made public from my [private Gist](https://gist.github.com/034fe1d3b0a5c9952eba).
  19. @yorkxin yorkxin revised this gist Jul 7, 2012. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -173,4 +173,5 @@ element.getAttributeNode("data-cat-weight").nodeValue; // => "100t"

    1. [Gecko DOM Reference - MDN](https://developer.mozilla.org/en/Gecko_DOM_Reference)
    - [Statements - JavaScript Reference - MDN](https://developer.mozilla.org/en/JavaScript/Reference#s26545)
    - [HTML5 Cross Browser Polyfills · Modernizr/Modernizr Wiki](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills)
    - [WWDC 2012 Session 601](https://developer.apple.com/videos/wwdc/2012/?id=601) (I learnt `classList` and `querySelectorAll` in the video)
  20. @yorkxin yorkxin created this gist Jul 7, 2012.
    176 changes: 176 additions & 0 deletions avoid-jquery-when-possible.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,176 @@
    # Avoid jQuery When Possible

    jQuery does good jobs when you're dealing with browser compatibility. But we're living in an age that fewer and fewer people use old-school browsers such as IE <= 7. With the growing of DOM APIs in modern browsers (including IE 8), most functions that jQuery provides are built-in **natively**.

    When targeting only modern browsers, it is better to avoid using jQuery's backward-compatible features. Instead, use the native DOM API, which will make your web page run much faster than you might think (native C / C++ implementaion v.s. JavaScript).

    If you're making a web page for iOS (e.g. UIWebView), you should use native DOM APIs because mobile Safari is not that old-school web browser; it supports lots of native DOM APIs.

    If you're making a Chrome Extension, you should **always** use native APIs, not only because Chrome has almost the latest DOM APIs available, but this can also avoid performance issue and unnecessary memory occupation (each extension needs a separate memory space for its jQuery; it is not shared).

    ## `$("#header")``getElementById()`

    As long as you know the actual ID of the target element, why don't you use [`getElementById`](https://developer.mozilla.org/en/DOM/document.getElementById)?

    ```javascript
    document.getElementById("header");
    // instead of $("#header");
    ```

    This works for IE >= 5.5

    ## `$(selector)` & `.find()``querySelectorAll()`

    Use [`querySelectorAll()`](https://developer.mozilla.org/en/DOM/Element.querySelectorAll) instead:

    ```javascript
    document.querySelectorAll("#header ul li");
    // instead of $("#header ul li")

    // or when you're finding in the context of #header

    var header = document.getElementById("header");
    header.querySelectorAll("ul li");
    // instead of $("#header").find("ul li");
    ```

    This works for IE >= 8.

    ## `$("<a class='blah' href='…'>hi</a>")``createElement()`

    [`jQuery(html)`](http://api.jquery.com/jQuery/) is useful when you're doing DOM tree manipulation with that html snippet. But if you only have to create a new element, use the native DOM API [`createElement`](https://developer.mozilla.org/en/DOM/document.createElement) instead:

    ```javascript
    var element = document.createElement("a");
    element.href = "http://www.example.com/";
    element.className = "blah";
    element.innerText = "hi";
    ```

    The [`element.className`](https://developer.mozilla.org/en/DOM/element.className) lets you access the `class` attribute of that element. More about this in the section below.

    ## `class` manipulation: use `classList` & `className`

    jQuery provides some [class manipulation functions](http://api.jquery.com/category/manipulation/class-attribute/) such as `addClass()`, `removeClass()`, `toggleClass()` as well as `hasClass()`. With the new DOM API `classList`, we can avoid these functions.

    Unfortunately IE <= 9 does not support `classList`. In this case you can use polyfills like [classList.js](https://github.com/eligrey/classList.js) or [this polyfill for IE 9](https://gist.github.com/1381839), or implement it manually by tokenizing the `className` string into an array, just like [what jQuery does](https://github.com/jquery/jquery/blob/master/src/attributes.js#L36).

    ### `classList` Usage

    ```javascript
    var element = document.getElementById("header");

    // add class
    element.classList.add("holiday-special");
    // instead of $(element).addClass("holiday-special")

    // for IE <= 9, we can do this: // mind the space!
    element.className += " holiday-special";

    // remove class
    element.classList.remove("holiday-special");
    // instead of $(element).removeClass("holiday-special")

    // toggle a class
    element.classList.toggle("hide");
    // instead of $(element).toggleClass("hide")

    // test if the element has some class
    element.contains("hide");
    // instead of $(element).hasClass("hide")
    ```

    ## `.css()``style` attribute & `style.cssText`

    jQuery's [`.css()`](http://api.jquery.com/css/) is useful for style manipulation (merging one set into another set). But if you only have to assign one attribute or override the whole style rule, don't use it.

    Alternatives:

    ### Assigning One Attribute

    e.g. Width

    ```javascript
    element.style.width = "320px";
    // instead of $(element).css("width", "320px");
    ```

    ### Overriding the Whole Style Rule

    Write the style directly with [`element.style.cssText=`](https://developer.mozilla.org/en/DOM/cssRule.cssText). Note that this is just like writing inline style directly, so mind that semicolon `;`.

    e.g. Width = 320px, Height = 640px

    ```javascript
    element.style.cssText = "width: 320px; height: 640px;"; // Mind the semicolon
    // instead of $(element).css({"width": "320px", "height": "640px"});
    ```

    ## `$.each()` → use `for` statement

    jQuery's [`$.each()`](http://api.jquery.com/jQuery.each/) is convenient when dealing with both `Array` and `Object` types, but why are we so lazy to know the actual data type?

    Although `$.each()` doesn't often create performance issue, we can use native `for` loop (for array) and `for..in` iterator (for object).

    The only benefit of `$.each` is that it uses a callback, so if you need an isolated variable scope, then `$.each` is better.

    ## `.data()` → use HTML5 `dataset`

    jQuery's [`.data()`](http://api.jquery.com/data/) implements its own storage, which may cause lots of overhead.

    If you're only supporting browsers with native [HTML5 DataSet](http://caniuse.com/#feat=dataset) implementation, use [`element.dataset`](https://developer.mozilla.org/en/DOM/element.dataSet) and its corresponding API to access the bounded data. Also see HTML5 Doctor's [Guide on HTML5 data-* Attributes](http://html5doctor.com/html5-custom-data-attributes/).

    Say that we have the following HTML:

    ```html
    <div id="long-cat" data-cat-length="30km">Nyan</div>
    ```

    Access the `cat-length` data with `dataset`:

    ```javascript
    var element = document.getElementById("long-cat");

    // read the existing data:
    element.dataset.catLength; // => "30km"

    // write the existing data:
    element.dataset.catLength = "42km";
    element.dataset.catLength; // => "42km"

    // same way to create a new data dynamically:
    element.dataset.catWeight = "100t";
    element.dataset.catWeight; // => "100t"
    ```

    Note that the name of data will be converted to camelCase, see [the spec](http://dev.w3.org/html5/spec/global-attributes.html#embedding-custom-non-visible-data-with-the-data-attributes) for more details.

    ### Support for Old Browsers

    If you need to support old browsers, take a look at [this polyfill](http://eligrey.com/blog/post/html-5-dataset-support), or use [`createAttribute`](https://developer.mozilla.org/en/DOM/document.createAttribute) and [`setAttributeNode`](https://developer.mozilla.org/en/DOM/element.setAttributeNode):

    ```javascript
    // use the same HTML structure above

    var element = document.getElementById("long-cat");

    // read the existing data:
    element.getAttributeNode("data-cat-length").nodeValue; // => "30km";

    // write the existing data:
    element.getAttributeNode("data-cat-length").nodeValue = "42km";
    element.getAttributeNode("data-cat-length").nodeValue// => "42km";

    // create a new data dynamically:
    var attribute = document.createAttribute("data-cat-weight");
    attribute.nodeValue = "100t";

    element.setAttributeNode(attribute);
    element.getAttributeNode("data-cat-weight").nodeValue; // => "100t"
    ```

    ## References

    1. [Gecko DOM Reference - MDN](https://developer.mozilla.org/en/Gecko_DOM_Reference)
    - [Statements - JavaScript Reference - MDN](https://developer.mozilla.org/en/JavaScript/Reference#s26545)
    - [WWDC 2012 Session 601](https://developer.apple.com/videos/wwdc/2012/?id=601) (I learnt `classList` and `querySelectorAll` in the video)