-1

I am used to using jQuery for manipulating the DOM, for example:

var mything = $("#mything");
mything.on("click", function() {
    mything.addClass("red");
    mything.html("I have sinned.");
});

But now I want to do the same things with Vanilla JavaScript. Is this possible? How can I do it?

Notice: This question is intended to be a comprehensive resource on Vanilla JavaScript DOM manipulation.

bjb568
  • 11,089
  • 11
  • 50
  • 71
  • 4
    Do you have some questions in mind that would be closed as duplicate of this? – Bergi Apr 13 '15 at 01:02
  • 2
    @Bergi Anything related to basic DOM manipulation. Like "How can I change the text somewhere when something is clicked?" or "Why doesn't document.getElementsByClassName().forEach work?" or similar. – bjb568 Apr 13 '15 at 01:06
  • 1
    Uh, but that really is too broad for a single question. I thought you were targeting "*How to write this jQuery code with vanilla JS*" or so only. Can you link specific already-asked questions that I might close? – Bergi Apr 13 '15 at 01:10
  • 6
    possible duplicate of [What do people mean when they say "DOM Manipulation"?](http://stackoverflow.com/questions/3934826/what-do-people-mean-when-they-say-dom-manipulation) – Qantas 94 Heavy Apr 13 '15 at 01:26
  • 4
    @Qantas94Heavy No, that's *barely tangentially related*. – bjb568 Apr 13 '15 at 01:27
  • 2
    @bjb568: you're asking how to manipulate the DOM. That question answers your "canonical" question. – Qantas 94 Heavy Apr 13 '15 at 01:42
  • @Qantas94Heavy "What is the DOM" ≠ "How can I manipulate it with Vanilla JS" – bjb568 Apr 13 '15 at 01:47
  • → [meta post](http://meta.stackoverflow.com/q/290054/1048572) discussing this question – Bergi Apr 13 '15 at 21:11

1 Answers1

21

When manipulating the Document Object Model with Vanilla JS, you will be directly be accessing the Document and Nodes. A document contains Elements, particularly HTMLElements and SVGElements which are both Nodes. An Element may contain Text too.

Finding Elements

You can get the first element which matches a CSS selector with mynode.querySelector(), and all elements that match the selector with myNode.querySelectorAll(). Most of the time myNode will be Document, so you can get anything in the document which matches the selector – however, you can look through only a node's descendants when myNode is a an element.

document.querySelectorAll('p:hover'); // Returns a NodeList of hovered paragraphs

This is similar to jQuery('p:hover').

There are also more specialized methods like:

Which have self-explanatory names. Notice that .getElementBy... returns a single element while .getElementsBy... (plural elements) returns a NodeList, which is essentially an array of nodes, but it doesn't have the standard array methods.

See also: What's the best way to loop through a set of elements in JavaScript?

Each element may also have a:

And NodeLists of:

In this way, we can traverse the DOM.

For example, to get the last child of the first paragraph element in the parent of #clickme here:

document.getElementById('clickme').addEventListener('click', function() {
  console.log(this.parentNode.getElementsByTagName('p')[0].lastChild);
});
<div>
  <blockquote>This is a really great quote.</blockquote>
  <p>This is a <em>really</em> interesting paragraph. <span>this will be selected</span></p>
  <p>In fact, here's another!</p>
  <button id="clickme">Click me!</button>
</div>

...you find its parentNode, use getElementsByTagName on that to only get paragraph descendants, take the first one of those, and get its lastChild.

To get the text contained in it, you could get its text node (its first child) then use text.wholeText.

Creating & Deleting

You can create an element with document.createElement('aTagName') or clone another one with newElement = myElement.cloneNode(). Pass cloneNode true as it's first argument to also duplicate its descendants. Don't clone elements with an ID because it will cause 2 elements with the same ID to appear in the same document.

You can then append the new element (or an existing one) to a parent element using parent.appendChild(newElement) or append it after another element with parent.insertBefore(newElement, referenceElement). An insertAfter method doesn't exist, but it can be created:

HTMLElement.prototype.insertAfter = function(newEl, refEl) {
  if (refEl.nextSibling) refEl.parentNode.insertBefore(newEl, refEl.nextSibling);
  else refEl.parentNode.appendChild(newEl);
};

A node can be removed with parent.removeChild() or replaced with parent.replaceChild(newChild) or just removed inline with mynode.remove().

function poof() {
  this.remove();
}
var elements = document.getElementsByClassName('poof');
for (var i = 0; i < elements.length; i++) {
  elements[i].addEventListener('click', poof);
}
<span class="poof">hi,</span>
<span class="poof">click</span>
<span class="poof">to</span>
<span class="poof">delete</span>
<span class="poof">me;</span>
<span class="poof">it</span>
<span class="poof">was</span>
<span class="poof">fun</span>
<span class="poof">being</span>
<span class="poof">a</span>
<span class="poof">span</span>

Classes and styles

By "and styles," I mean just classes. Styles are for CSS. You can apply CSS styles only to elements who have had a class added with JavaScript.1

Elements in HTML have a classList property which is a DOMTokenList representing a space-separated property, in this case class. You can .add(), .remove(), and .toggle() classes in the classList or check if it .contains() a class.

document.getElementById('clickme').addEventListener('click', function() {
  document.getElementById('colors').classList.toggle('green');
});
.green { color: green }
<div id="colors">hello!</div>
<button id="clickme">Click me!</button>

Attributes

Elements with certain attributes can be selected with querySelector and querySelectorAll. Most attributes are properties of the element you're working with already. For example:

myDiv.hidden = true; // Hides element from view and from screenreaders

But if they're not, any attribute can be accessed with getAttributeNode, setAttributeNode, and removeAttributeNode. AttributeNodes have ownerElements and values.

"data-*" attributes can be accessed with myelement.dataset. For example, mydiv.dataset.pie = 'yummy' would add data-pie="yummy" to the div.

Events

Events are slightly more complicated. Binding one (like jQuery('selector').on) is pretty easy:

myElement.addEventListener('event-name', afunction);

(Other objects also have this method – for example, window)

Events can also be removed:

myelement.removeEventListener('event-name', afunction);

See: removeEventListener

An event list can be found here.

The function passed to the addEventListener will be passed an argument of the event occurring and have a this of the element the event listener is bound to.

However, events aren't this simple: something as trivial as clicking on a button may fire many event listeners on different elements and for different events.

Click event path on iOS

Browser Input Events: Can We Do Better Than The Click? by Smashing Magazine

See also: What is event bubbling and capturing?

1 If you really need to modify a style with JS, use myElement.style.styleProperty = 'value' to change the inline style attribute.

Community
  • 1
  • 1
bjb568
  • 11,089
  • 11
  • 50
  • 71
  • 2
    This would be a lot easier to take in for someone new to this if there were clearly listed (close to) equivalents for common jQuery methods. – Qantas 94 Heavy Apr 13 '15 at 01:33
  • @Qantas94Heavy I edited in 2, but I'm not well-versed in jQuery. You can add more jQuery equivalents yourself (community-wiki answer). – bjb568 Apr 13 '15 at 01:38
  • @Qantas94Heavy [YouMightNotNeedjQuery](http://youmightnotneedjquery.com/) is around for a while, you could reference it for more info. – nrodic Apr 13 '15 at 22:47