As useful a library as jQuery is, sometimes it seems overkill to load a large library in order to pull off simple stuff like adding an event listener or toggling a class. With many native methods now available and at times being much faster, it's never been a better time to step away from jQuery for a little while and learn some of it's native equivalents.

In the following series of posts I'll show you some useful snippets which allow you to do the things that you would normally use jQuery for without the need to add another 80kB+ to your page. Each post will show the jQuery method with the corresponding native method. The posts won't be the be-all and end-all of tutorials, but should be treated as useful quick-reference guides.

Part 2: Working with Attributes and Properties

Part 3: Event Listeners

Part 4: Creating, inserting, moving and removing elements

This post will focus on the multitude of methods for selecting DOM elements.

Get an element by it's ID

jQuery
  $('#myElement')

Native
  document.getElementById('myElement') // IE 5.5+
// or
document.querySelector('#myElement') // IE 8+

Note that the jQuery method returns an object, whereas the native versions return a DOM node.

Get elements by their tagName

jQuery
  $('div')

Native
  document.getElementsByTagName('div')
// or
document.querySelectorAll('div')

Get elements by their className

jQuery
  $('.some-class')

Native
  document.getElementsByClassName('some-class')
// or
document.querySelectorAll('.some-class')

Note that like jQuery's $() method, querySelector and querySelectorAll both accept CSS3 selector strings.

Another important thing to note is that all methods in the above examples start their search from the top of the DOM tree which may not be efficient when dealing with a ton of DOM nodes. We can, however, call the methods directly on a DOM node.

Example:

  var parent = document.getElementById('myElement');
parent.getElementsByClassName('child-class');

This will start the search directly from the parent node and return all it's descendant elements with className 'child-class'.

So, which one do I use?

OK, we have getElementsByTagName, getElementsByClassName and querySelectorAll so which one do you use?

Although the querySelectorAll method is the more modern approach and is closer to jQuery's $(selector) method as it allows the use of a wide range of CSS3 selectors, there's one major difference that may come back and bite you in the behind if you're not aware of it.

The querySelectorAll method, like jQuery's $(selector) method, will return a static NodeList, whereas getElementsByTagName and getElementsByClassName will both return a live NodeList.

Okay, so what's the difference?

Think of a static NodeList as a snapshot of the DOM as it was when the querySelectorAll method was called. Anything you do to the DOM after the querySelectorAll method was called will not be reflected in the NodeList it returned. Whereas the live NodeList returned by both getElementsByTagName and getElementsByClassName will.

Why is that important?

Take the following list of items:

  <ul>
    <li>Item</li>
    <li>Item</li>
    <li>Item</li>
    <li>Item</li>
</ul>

Let's get the items using the querySelectorAll method:

  var myItems = document.querySelectorAll('li');

Now let's count those items:

  console.log(myItems.length) // returns 4;

Now let's add a new item to the list:

  // Create a new <li> element
var newItem = document.createElement('li');
// And append it to the list
myItems[0].parentNode.appendChild(newItem);

Now let's count the items again:

  console.log(myItems.length) // returns 4 again;

Uh-oh, what's happened? We now have 5 items in the list, but the second count still shows 4 items!

This is the essence of a static NodeList and the one major difference you need to be aware of when using querySelectorAll over getElementsByTagName and getElementsByClassName. As both of the latter methods return a live NodeList, the count on the second try would have correctly returned 5. In fact, no matter how many elements you add or remove, the latter methods will always reflect the changes.

Of course this only applies if the DOM changes after the method is called so, if you're confident that no changes will occur in the future, you're safe to use the querySelectorAll method.

A working example of the above can be found here: http://codepen.io/Mobius1/pen/jMjjZd.

Further reading: getElementById, getElementsByTagName, getElementsByClassName, querySelector, querySelectorAll

More Selectors

Get the first element of it's type

jQuery
  $('div:first-child')
// or
$('div').first();

Native
  document.getElementsByTagName('div')[0]
// or
document.querySelectorAll('div')[0]
// or
document.querySelector('div')

Remember you can replace document with a DOM node to make the search more efficient.

Also note that querySelector returns the first matching Element node whilst querySelectorAll returns a NodeList.

Get an element's child nodes

jQuery
  $('div').children();

Native
  document.getElementsByTagName('div').childNodes // IE < 9
// or
document.getElementsByTagName('div').children // IE 9+

Do not use the childNodes method unless you know what you're doing or you need to support IE < 9 as this can return non element nodes like text nodes.

Get the first child of an element

jQuery
  $('#myList > li:first-child');
// or
$('#myList').children('li:first-child');
// or
$('#myList > li').first();
// or
$('#myList').children().first();
// and so on

Native
  var myList = document.getElementById('myList');
myList.children[0] // IE 9+
// or
myList.firstChild // IE < 9
// or
myList.firstElementChild // IE 9+

The firstChild method comes with the same pitfalls as childNodes.

Remember that like jQuery's $() method, querySelector and querySelectorAll both support complex CSS3 selectors so the following can be used as well:

  document.querySelector('ul').querySelector('li');
// or
document.querySelector('ul > li:first-child');
// or 
document.querySelector('ul > li')
// and so on

As with jQuery, there are a multitude of ways of getting the desired node in the above example so just pick the one that suits your needs, but it should be noted that (depending on the browser used) the older getElementsByTagName and getElementsByClassName methods can be much faster that both the newer native methods and the jQuery method: run test

Get the last child of an element

jQuery
  $('#myList > li:last-child');
// or
$('#myList').children('li:last-child');
// or
$('#myList > li').last();
// or
$('#myList').children().last();
// and so on

Native
  var myList = document.getElementById('myList');
myList.lastChild // IE < 9
// or
myList.children[myList.length - 1] // IE 9+
// or simply
myList.lastElementChild // IE 9+

As with the firstChild and childNodes methods, the lastChild method should only be used if you have to support IE < 9.

Further reading: firstChild, lastChild, childNodes, nodeType, children, firstElementChild, lastElementChild

Get an element's parent element

jQuery
  myElement.parent();

Native
  myElement.parentNode

Get the element immediately following an element

jQuery
  myElement.next();

Native
  myElement.nextSibling // IE < 9
// or
myElement.nextElementSibling // IE 9+

The native methods will return null if myElement is the last child of it's parent.

Get the element immediately preceding an element

jQuery
  myElement.prev();

Native
  myElement.previousSibling // IE < 9
// or
myElement.previousElementSibling // IE 9+

The native methods will return null if myElement is the first child of it's parent.

The nextSibling and previousSibling methods should be treated the same as childNodes, firstChild and lastChild for their potential for returning nodes other than HTMLElements.

If you need to use any of these methods to support older browsers then you can check the nodeType to be safe. A HTMLElement's nodeType is 1, so if the nodeType of a node is something other that 1, you know you don't have a HTMLElement.

Example:

  if ( myElement.nodeType > 1 ) {
    // myElement is not an element!
}

Further reading: parentNode, previousSibling, nextSibling, previousElementSibling, nextElementSibling

No native alternative?

DOM traversal can be a daunting task (especially to newcomers) and thankfully JQuery has a lot of useful methods to help ease the pain. However, not all jQuery methods have a native equivalent.

Get the closest element up the DOM tree (ancestor) matching a selector

jQuery
  // Get the first ancestor with the className 'some-class'
var closestElem = $('#myElement').closest('.some-class');

Native

Although there is a native version of jQuery's closest() method, it's experimental and therefore shouldn't be used in production.

Further reading: Element.closest()

Let's make our own instead.

  function closest(el, selector) {
    var first = selector.charAt(0);
    // Traverse the DOM tree using parentNode
    for ( ; el && el !== document && el.nodeType === 1; el = el.parentNode ) {
        // Selector is a class
        if ( first === '.' && el.classList.contains( selector.substr(1) ) ) {
            return el;
        }
        // Selector is a tagName
        if ( el.tagName.toLowerCase() === selector ) {
            return el;
        }
    }
    return null;
};

var closestElem = closest(document.getElementById('#myElem'), '.some-class'));

The above function omits the check for more complex attributes like the data-* attribute for brevity and also omits a check for an ID as one would generally call getElementById to find it.

That's it for now. Tune in next week when we'll be looking at native methods of jQuery's attr(), prop(), addClass(), hasClass(), removeClass() and methods of 'polyfilling' for older browsers.

Part 2: Working with Attributes and Properties Part 3: Event Listeners

Part 4: Creating, inserting, moving and removing elements


2,654 0 67