https://github.com/alperg/dom
JavaScript DOM traversal and manipulation Student guide
https://github.com/alperg/dom
Last synced: 3 months ago
JSON representation
JavaScript DOM traversal and manipulation Student guide
- Host: GitHub
- URL: https://github.com/alperg/dom
- Owner: alperg
- License: mit
- Created: 2020-01-22T23:57:23.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2020-01-23T01:14:28.000Z (over 5 years ago)
- Last Synced: 2024-12-28T20:44:09.021Z (5 months ago)
- Size: 6.84 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# JavaScript DOM traversal and manipulation Student guide
## DOM ready and window load
```js
// $(document).ready(function() {...});
document.addEventListener('DOMContentLoaded', function() {...});// $(window).load(function() {...});
window.addEventListener('load', function() {...});
window.onload = function() {...};
```## Selectors
```js
// var divs = $('ul.nav > li');
var items = document.querySelectorAll('ul.nav > li');
var firstItem = document.querySelector('ul.nav > li');// var title = $('#title');
var title = document.getElementById('title');// var images = $('.image');
var images = document.getElementsByClassName('image');// var articles = $('article');
var articles = document.getElementsByTagName('article');
```| Selector | Returns a collection | Returns a LIVE collection | Return type | Built-in forEach | Works with any root element |
|----------|----------------------|---------------------------|-------------|------------------|-----------------------------|
| `getElementById` | 🚫 | N/A | Reference to an `Element` object or `null` | N/A | 🚫 |
| `getElementsByClassName` | ✅ | ✅ | `HTMLCollection` | 🚫 | ✅ |
| `getElementsByTagName` | ✅ | ✅ | `HTMLCollection` according to the spec (`NodeList` in WebKit) (*) | 🚫 | ✅ |
| `querySelector` | 🚫 | N/A | `Element` object or `null` | N/A | ✅ |
| `querySelectorAll` | ✅ | 🚫 | Static `NodeList` of `Element` objects | ✅ | ✅ |
(*) *The [latest W3C specification](https://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html) says it returns an `HTMLCollection`; however, this method returns a [`NodeList`](https://developer.mozilla.org/en-US/docs/Web/API/NodeList) in WebKit browsers. See [bug 14869](https://bugzilla.mozilla.org/show_bug.cgi?id=14869) for details.*Since all of the selectors (except `getElementById`) support querying a node other than `document`, finding results trivial:
```js
// $(el).find(selector);
el.querySelectorAll(selector);
```## Creating elements
```js
// var newDiv = $('');
var newDiv = document.createElement('div');// There is no equivalent in jQuery for createTextNode.
// You can always use the DOM method, or write a jQuery wrapper around it.
// The closest thing you may be able to find is when creating new elements,
// you can specify the text part separately.
// var newTextNode = $('', { text: 'hello world' });
var newTextNode = document.createTextNode('hello world');
```## Adding elements to the DOM
```js
// $(parent).append(el);
parent.appendChild(el);// $(parent).prepend(el);
parent.prepend(el);// $(parent).prepend(el);
parent.insertBefore(el, parent.firstChild);
el.insertBefore(node);// $(el).before(htmlString);
el.insertAdjacentHTML('beforebegin', htmlString);// $(el).after(htmlString);
el.insertAdjacentHTML('afterend', htmlString);
```## Traversing the DOM
```js
// $(el).children();
el.children // only HTMLElements
el.childNodes // includes comments and text nodes
// You can't `forEach` through `children` unless you turn it into an array first.// $(el).parent();
el.parentNode// $(el).closest(selector);
el.closest(selector);// $(el).first();
el.firstElementChild; // only HTMLElements
el.firstChild; // includes comments and text nodes// $(el).last();
el.lastElementChild; // only HTMLElements
el.lastChild; // includes comments and text nodes// First and last alternative
var nodeList = document.querySelectorAll('.some-class');
var first = nodeList[0];
var last = nodeList[nodeList.length - 1];// $(el).siblings();
[].filter.call(el.parentNode.children, function(child) {
return child !== el;
});// $(el).prev();
el.previousElementSibling; // only HTMLElements
el.previousSibling; // includes comments and text nodes// $(el).next();
el.nextElementSibling; // only HTMLElements
node.nextSibling; // includes comments and text nodes// $.contains(el, child);
el !== child && el.contains(child);
```## Traversing a node list
```js
var nodes = document.querySelectorAll('.class-name');// 1.
var elements = Array.prototype.slice.call(nodes);
elements.forEach(noop);// 2. (clean, but creates a new array)
[].forEach.call(nodes, noop);// 3.
Array.prototype.forEach.call(nodes, noop);
```## Closest
Find the closest element that matches the target selector:
```js
var node = document.getElementById('my-id');
var isFound = false;while (node instanceof Element) {
if (node.matches('.target-class')) {
isFound = true;
break;
}
node = node.parentNode;
}
```## Removing and replacing nodes
```js
// $(el).remove();
el.parentNode.removeChild(el);
el.remove();// Remove all GIF images from the page
[].forEach.call(document.querySelectorAll('img'), function (img) {
if (/\.gif/i.test(img.src)) {
img.remove();
}
});// $(el).replaceWith($('.first'));
el.parentNode.replaceChild(newNode, el);// $(el).replaceWith(string);
el.outerHTML = string;
```## Cloning nodes
```js
// $(el).clone();
el.cloneNode(true);
```Pass in `true` to also clone child nodes.
## Checking if a node is empty
```js
// $(el).is(':empty');
!el.hasChildNodes();
```## Emptying an element
```js
// $(el).empty();
var el = document.getElementById('el');// This is faster
while (el.firstChild) {
el.removeChild(el.firstChild);
}
```You could also do `el.innerHTML = ''`, but would be slower than the above method.
## Comparing elements
```js
// $(el).is($(otherEl));
el === otherEl// $(el).is('.my-class');
el.matches('.my-class');// $(el).is('a');
el.matches('a');
```## Getting and setting text content
```js
// $(el).text();
el.textContent// $(el).text(string);
el.textContent = string;
```There's also `innerText` and `outerText`:
* `innerText` was non-standard, while `textContent` was standardised earlier.
* `innerText` returns the visible text contained in a node, while `textContent` returns the full text. For example, on the following element: `Hello World`, `innerText` will return 'Hello', while `textContent` will return 'Hello World'. As a result, `innerText` is much more performance-heavy: it requires layout information to return the result.Here is the official warning for `innerText`: *This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.*
## Getting and setting outer/inner HTML
```js
// $('').append($(el).clone()).html();
el.outerHTML;// $(el).replaceWith(string);
el.outerHTML = string;// $(el).html();
el.innerHTML// $(el).html(string);
el.innerHTML = string;// $(el).empty();
el.innerHTML = '';
```## Getting and setting attributes
```js
// $(el).attr('tabindex');
el.getAttribute('tabindex');// $(el).attr('tabindex', 3);
el.setAttribute('tabindex', 3);
```
Since elements are just objects, most of the times we can directly access (and set) their properties:```js
var oldId = el.id;
el.id = 'foo';
```Some other properties we can access directly:
```js
node.style;
node.class;
node.href;
node.checked;
node.disabled;
node.selected;
```For data attributes we can either use `el.getAttribute('data-something')` or resort to the `dataset` object:
```js
// $(el).data('camelCaseValue');
string = element.dataset.camelCaseValue;// $(el).data('camelCaseValue', 'foo');
element.dataset.camelCaseValue = 'foo';
```## Styling elements
```js
// $(el).css('background-color', '#3cca5e');
el.style.backgroundColor = '#3cca5e';// $(el).hide();
el.style.display = 'none';// $(el).show();
el.style.display = '';
```To get the values of all CSS properties for an element you should use `window.getComputedStyle(element)` instead:
```js
// $(el).css(ruleName);
getComputedStyle(el)[ruleName];
```## Working with CSS classes
```js
// $(el).addClass('foo');
el.classList.add('foo');// $(el).removeClass('foo');
el.classList.remove('foo');// $(el).toggleClass('foo');
el.classList.toggle('foo');// $(el).hasClass('foo');
el.classList.contains('foo');
```## Getting the position of an element
```js
// $(el).outerHeight();
el.offsetHeight// $(el).outerWidth();
el.offsetWidth// $(el).position();
{ left: el.offsetLeft, top: el.offsetTop }// $(el).offset();
var rect = el.getBoundingClientRect();
{
top: rect.top + document.body.scrollTop,
left: rect.left + document.body.scrollLeft
}
```## Binding events
```js
// $(el).on(eventName, eventHandler);
el.addEventListener(eventName, eventHandler);// $(el).off(eventName, eventHandler);
el.removeEventListener(eventName, eventHandler);
```If working with a collection of elements, you can bind an event handler to each one of them by using a loop:
```js
// $('a').on(eventName, eventHandler);
var links = document.querySelectorAll('a');
[].forEach.call(links, function (link) {
link.addEventListener(eventName, eventHandler);
});
```## Event delegation
Can add to higher element and use 'matches' to see if specific child was clicked (similar to jQuery's `.on`):
```js
// $('ul').on('click', 'li > a', eventHandler); // Pay attention to the second argument here
var el = document.querySelector('ul');
el.addEventListener('click', event => {
if (event.target.matches('li')) {
// event handling logic
}
});
```## The event object
```js
var node = document.getElementById('my-node');
var onClick = function (event) {
// this = element// can filter by target = event delegation
if (!event.target.matches('.tab-header')) {
return;
}// stop the default browser behaviour
event.preventDefault();// stop the event from bubbling up the dom
event.stopPropagation();// other listeners on this node will not fire
event.stopImmediatePropagation();
};node.addEventListener('click', onClick);
node.removeEventListener('click', onClick);
```## Animations
```js
// $(el).fadeIn();
function fadeIn(el) {
el.style.opacity = 0;var last = +new Date();
var tick = function () {
el.style.opacity = +el.style.opacity + (new Date() - last) / 400;
last = +new Date();if (+el.style.opacity < 1) {
(window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
}
};tick();
}
```Or, if you are only supporting IE10+:
```js
el.classList.add('show');
el.classList.remove('hide');
``````css
.show {
transition: opacity 400ms;
}.hide {
opacity: 0;
}
```## Looping over and filtering through collections of DOM elements
Note this is not necessary if you are using `querySelector(All)`.
```js
// $(selector).each(function (index, element) { ... });
var elements = document.getElementsByClassName(selector);[].forEach.call(elements, function (element, index, arr) { ... });
//or
Array.prototype.forEach.call(elements, function (element, index, array) { ... });// or
Array.from(elements).forEach((element, index, arr) => { ... }); // ES6
```Same concept applies to filtering:
```js
// $(selector).filter(":even");
var elements = document.getElementsByClassName(selector);[].filter.call(elements, function (element, index, arr) {
return index % 2 === 0;
});
```Recall that `:even` and `:odd` use 0-based indexing.
Another filtering example:
```js
var nodeList = document.getElementsByClassName('my-class');
var filtered = Array.prototype.filter.call(nodeList, function (item) {
return item.innerText.indexOf('Item') !== -1;
});
```## Stringify & Parse
```js
JSON.stringify(object);// $.parseJSON(string);
JSON.parse(string);// $.trim(string);
string.trim();
```