{"id":20806786,"url":"https://github.com/fed/dom","last_synced_at":"2026-02-23T09:39:01.062Z","repository":{"id":85048373,"uuid":"102635164","full_name":"fed/dom","owner":"fed","description":"Traversing and Manipulating the DOM with VanillaJS","archived":false,"fork":false,"pushed_at":"2023-08-21T09:38:40.000Z","size":23,"stargazers_count":4,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-28T21:22:19.072Z","etag":null,"topics":["cheatsheet","dom","dom-traversal","vanilla-js"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fed.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-09-06T16:58:31.000Z","updated_at":"2023-11-16T20:43:48.000Z","dependencies_parsed_at":"2025-01-18T13:32:38.596Z","dependency_job_id":"8334d340-5334-48fa-976a-5758935fa865","html_url":"https://github.com/fed/dom","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fed/dom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fed%2Fdom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fed%2Fdom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fed%2Fdom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fed%2Fdom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fed","download_url":"https://codeload.github.com/fed/dom/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fed%2Fdom/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29741140,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-23T07:44:07.782Z","status":"ssl_error","status_checked_at":"2026-02-23T07:44:07.432Z","response_time":90,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cheatsheet","dom","dom-traversal","vanilla-js"],"created_at":"2024-11-17T19:26:43.983Z","updated_at":"2026-02-23T09:39:01.035Z","avatar_url":"https://github.com/fed.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# DOM Traversal and Manipulation with [VanillaJS](http://vanilla-js.com/)\n\n![DOM Traversal and Manipulation](https://i.imgur.com/t5rKLJy.jpg)\n\n## ⚠️ Heads up!\n\nI've moved this article/cheatsheet to my blog, to check the latest version [click here](https://fedknu.com/blog/dom-traversal-manipulation/).\n\n## DOM ready and window load\n\n```js\n// $(document).ready(callback);\ndocument.addEventListener('DOMContentLoaded', callback);\n\n// $(window).load(callback);\nwindow.addEventListener('load', callback);\nwindow.onload = callback;\n```\n\n## Selectors\n\n```js\n// const divs = $('ul.nav \u003e li');\nconst items = document.querySelectorAll('ul.nav \u003e li');\nconst firstItem = document.querySelector('ul.nav \u003e li');\n\n// const title = $('#title');\nconst title = document.getElementById('title');\n\n// const images = $('.image');\nconst images = document.getElementsByClassName('image');\n\n// const articles = $('article');\nconst articles = document.getElementsByTagName('article');\n```\n\n| Selector | Returns a collection | Returns a LIVE collection | Return type | Built-in forEach | Works with any root element |\n|----------|----------------------|---------------------------|-------------|------------------|-----------------------------|\n| `getElementById` | 🚫 | N/A | Reference to an `Element` object or `null` | N/A | 🚫 |\n| `getElementsByClassName` | ✅ | ✅ | `HTMLCollection` | 🚫 | ✅ |\n| `getElementsByTagName` | ✅ | ✅ | `HTMLCollection` according to the spec (`NodeList` in WebKit) (*) | 🚫 | ✅ |\n| `querySelector` | 🚫 | N/A | `Element` object or `null` | N/A | ✅ |\n| `querySelectorAll` | ✅ | 🚫 | Static `NodeList` of `Element` objects | ✅ | ✅ |\n  \n(*) *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.*\n\nSince all of the selectors (except `getElementById`) support querying a node other than `document`, finding results trivial:\n\n```js\n// $(el).find(selector);\nel.querySelectorAll(selector);\n```\n\n## jQuery-like selector\n\n```js\nfunction $$(selector) {\n  var nodes = document.querySelectorAll(selector);\n  return Array.prototype.slice.call(nodes);\n}\n\n$$('selector');\n```\n\n## Creating elements\n\n```js\n// $('\u003cdiv /\u003e');\nconst newDiv = document.createElement('div');\n\n// There is no equivalent in jQuery for createTextNode.\n// You can always use the DOM method, or write a jQuery wrapper around it.\n// The closest thing you may be able to find is when creating new elements,\n// you can specify the text part separately.\n// $('\u003cdiv\u003e', { text: 'hello world' });\nconst newTextNode = document.createTextNode('hello world');\n```\n\n## Adding elements to the DOM\n\n```js\n// $(parent).append(el);\nparent.appendChild(el);\n\n// $(parent).prepend(el);\nparent.prepend(el);\n// ⚠️ Heads up: needs to be polyfilled on IE and Edge.\n\n// $(parent).prepend(el); \nparent.insertBefore(el, parent.firstChild);\nel.insertBefore(node) // @TODO\n\n// $(el).before(htmlString);\nel.insertAdjacentHTML('beforebegin', htmlString);\n\n// $(el).after(htmlString);\nel.insertAdjacentHTML('afterend', htmlString);\n```\n\n## Traversing the DOM\n\n```js\n// $(el).children();\nel.children // only HTMLElements\nel.childNodes // includes comments and text nodes\n// ⚠️ Heads up: you can't `forEach` through `children` unless you turn it into an array first.\n\n// $(el).parent();\nel.parentNode\n\n// $(el).closest(selector);\nel.closest(selector);\n\n// $(el).first();\nel.firstElementChild; // only HTMLElements\nel.firstChild; // includes comments and text nodes\n\n// $(el).last();\nel.lastElementChild; // only HTMLElements\nel.lastChild; // includes comments and text nodes\n\n// First and last alternative\nvar nodeList = document.querySelectorAll('.some-class');\nvar first = nodeList[0];\nvar last = nodeList[nodeList.length - 1];\n\n// $(el).siblings();\n[].filter.call(el.parentNode.children, function (child) {\n  return child !== el;\n});\n\n// $(el).prev();\nel.previousElementSibling // only HTMLElements\nel.previousSibling // includes comments and text nodes\n\n// $(el).next();\nel.nextElementSibling // only HTMLElements\nnode.nextSibling // includes comments and text nodes\n\n// $.contains(el, child);\nel !== child \u0026\u0026 el.contains(child);\n```\n\n## Traversing a node list\n\n```js\nvar nodes = document.querySelectorAll('.class-name');\n\n// 1.\nvar elements = Array.prototype.slice.call(nodes);\nelements.forEach(noop);\n\n// 2. (clean, but creates a new array)\n[].forEach.call(nodes, noop);\n\n// 3.\nArray.prototype.forEach.call(nodes, noop);\n```\n\n## Closest\n\nFind the closest element that matches the target selector:\n\n```js\nvar node = document.getElementById('my-id');\nvar isFound = false;\n\nwhile (node instanceof Element) {\n  if (node.matches('.target-class')) {\n    isFound = true;\n    break;\n  }\n  node = node.parentNode;\n}\n```\n\n`Element.prototype.closest` Polyfill:\n\n```js\nif (Element \u0026\u0026 !Element.prototype.closest) {\n  Element.prototype.closest = function (selector) {\n    var el = this;\n    while (el instanceof Element) {\n      if (el.matches(selector)) {\n        return el;\n      }\n      el = el.parentNode;\n    }\n  }\n}\n```\n\n## Removing and replacing nodes\n\nHeads up: [Element.remove()](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove) needs to be polyfilled on IE.\n\n```js\n// $(el).remove();\nel.parentNode.removeChild(el);\nel.remove(); ⚠️\n\n// Remove all GIF images from the page\n[].forEach.call(document.querySelectorAll('img'), function (img) {\n  if (/\\.gif/i.test(img.src)) {\n    img.remove();\n  }\n});\n\n// $(el).replaceWith($('.first'));\nel.parentNode.replaceChild(newNode, el);\n\n// $(el).replaceWith(string);\nel.outerHTML = string;\n```\n\n## Cloning nodes\n\n```js\n// $(el).clone();\nel.cloneNode(true);\n```\n\nPass in `true` to also clone child nodes.\n\n## Checking if a node is empty\n\n```js\n// $(el).is(':empty')\n!el.hasChildNodes();\n```\n\n## Emptying an element\n\n```js\n// $(el).empty();\nconst el = document.getElementById('el');\n\nwhile (el.firstChild) {\n  el.removeChild(el.firstChild);\n}\n```\n\nYou could also do `el.innerHTML = ''`.\n\n## Comparing elements\n\n```js\n// $(el).is($(otherEl));\nel === otherEl\n\n// $(el).is('.my-class');\nel.matches('.my-class');\n\n// $(el).is('a');\nel.matches('a');\n```\n\nNote that `matches` needs to be polyfilled in older browsers. Also, many browsers implement [`Element.matches`](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches) prefixed, under the non-standard name `matchesSelector`. We can play safe by using something along the lines of:\n\n```js\nfunction matches(el, selector) {\n  return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector);\n};\n\nmatches(el, '.my-class');\n```\n\n## Getting and setting text content\n\n```js\n// $(el).text();\nel.textContent\n\n// $(el).text(string);\nel.textContent = string;\n```\n\nThere's also `innerText` and `outerText`:\n\n* `innerText` was non-standard, while `textContent` was standardised earlier.\n* `innerText` returns the visible text contained in a node, while `textContent` returns the full text. For example, on the following element: `\u003cspan\u003eHello \u003cspan style=\"display: none;\"\u003eWorld\u003c/span\u003e\u003c/span\u003e`, `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.\n\nHere 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.*\n\n## Getting and setting outer/inner HTML\n\n```js\n// $('\u003cdiv\u003e').append($(el).clone()).html();\nel.outerHTML;\n\n// $(el).replaceWith(string);\nel.outerHTML = string;\n\n// $(el).html();\nel.innerHTML\n\n// $(el).html(string);\nel.innerHTML = string;\n\n// $(el).empty();\nel.innerHTML = '';\n```\n\n## Getting and setting attributes\n\n```js\n// $(el).attr('tabindex');\nel.getAttribute('tabindex');\n\n// $(el).attr('tabindex', 3);\nel.setAttribute('tabindex', 3);\n```\n  \nSince elements are just objects, most of the times we can directly access (and set) their properties:\n\n```js\nconst oldId = el.id;\nel.id = 'foo';\n```\n\nSome other properties we can access directly:\n\n```js\nnode.href;\nnode.checked;\nnode.disabled;\nnode.selected;\n```\n\nFor data attributes we can either use `el.getAttribute('data-something')` or resort to the `dataset` object:\n\n```js\n// $(el).data('camelCaseValue');\nstring = element.dataset.camelCaseValue;\n\n// $(el).data('camelCaseValue', 'foo');\nelement.dataset.camelCaseValue = 'foo';\n```\n\n## Styling an element\n\n```js\n// $(el).css('background-color', '#3cca5e');\nel.style.backgroundColor = '#3cca5e';\n\n// $(el).hide();\nel.style.display = 'none';\n\n// $(el).show();\nel.style.display = '';\n```\n\nTo get the values of all CSS properties for an element you should use `window.getComputedStyle(element)` instead:\n\n```js\n// $(el).css(ruleName);\ngetComputedStyle(el)[ruleName];\n```\n\n## Working with CSS classes\n\n```js\n// $(el).addClass('foo');\nel.classList.add('foo');\n\n// $(el).removeClass('foo');\nel.classList.remove('foo');\n\n// $(el).toggleClass('foo');\nel.classList.toggle('foo');\n\n// $(el).hasClass('foo');\nel.classList.contains('foo');\n```\n\n## Getting the position of an element\n\n```js\n// $(el).outerHeight();\nel.offsetHeight\n\n// $(el).outerWidth();\nel.offsetWidth\n\n// $(el).position();\n{ left: el.offsetLeft, top: el.offsetTop }\n\n// $(el).offset();\nconst rect = el.getBoundingClientRect();\n\n{\n  top: rect.top + document.body.scrollTop,\n  left: rect.left + document.body.scrollLeft\n}\n```\n\n## Binding events\n\n```js\n// $(el).on(eventName, eventHandler);\nel.addEventListener(eventName, eventHandler);\n\n// $(el).off(eventName, eventHandler);\nel.removeEventListener(eventName, eventHandler);\n```\n\nIf working with a collection of elements, you can bind an event handler to each one of them by using a loop:\n\n```js\n// $('a').on(eventName, eventHandler);\nconst links = document.querySelectorAll('a');\n[].forEach.call(links, function (link) {\n  link.addEventListener(eventName, eventHandler);\n});\n```\n\n## Event delegation\n\nCan add to higher element and use 'matches' to see if specific child was clicked (similar to jQuery's `.on`):\n\n```js\n// $('ul').on('click', 'li \u003e a', eventHandler);\nconst el = document.querySelector('ul');\nel.addEventListener('click', event =\u003e {\n  if (event.target.matches('li')) {\n    // event handling logic\n  }\n});\n```\n\n## The event object\n\n```js\nvar node = document.getElementById('my-node');\nvar onClick = function (event) {\n  // this = element\n\n  // can filter by target = event delegation\n  if (!event.target.matches('.tab-header')) {\n    return;\n  }\n\n  // stop the default browser behaviour\n  event.preventDefault();\n\n  // stop the event from bubbling up the dom\n  event.stopPropagation();\n\n  // other listeners on this node will not fire\n  event.stopImmediatePropagation();\n};\n\nnode.addEventListener('click', onClick);\nnode.removeEventListener('click', onClick);\n```\n\n## Mocking events\n\n```js\nvar anchor = document.getElementById('my-anchor');\nvar event = new Event('click');\n\nanchor.dispatchEvent(event);\n```\n\n## Animations\n\n```js\n// $(el).fadeIn();\nfunction fadeIn(el) {\n  el.style.opacity = 0;\n\n  var last = +new Date();\n  var tick = function () {\n    el.style.opacity = +el.style.opacity + (new Date() - last) / 400;\n    last = +new Date();\n\n    if (+el.style.opacity \u003c 1) {\n      (window.requestAnimationFrame \u0026\u0026 requestAnimationFrame(tick)) || setTimeout(tick, 16);\n    }\n  };\n\n  tick();\n}\n```\n\nOr, if you are only supporting IE10+:\n\n```js\nel.classList.add('show');\nel.classList.remove('hide');\n```\n\n```css\n.show {\n  transition: opacity 400ms;\n}\n\n.hide {\n  opacity: 0;\n}\n```\n\n## Looping over and filtering through collections of DOM elements\n\nNote this is not necessary if you are using `querySelector(All)`.\n\n```js\n// $(selector).each(function (index, element) { ... });\nconst elements = document.getElementsByClassName(selector);\n\n[].forEach.call(elements, function (element, index, arr) { ... });\n\n//or\nArray.prototype.forEach.call(elements, function (element, index, array) { ... });\n\n// or\nArray.from(elements).forEach((element, index, arr) =\u003e { ... }); // ES6 ⚠️\n```\n\nSame concept applies to filtering:\n\n```js\n// $(selector).filter(\":even\");\nconst elements = document.getElementsByClassName(selector);\n\n[].filter.call(elements, function (element, index, arr) {\n  return index % 2 === 0;\n});\n```\n\nRecall that `:even` and `:odd` use 0-based indexing.\n\nAnother filtering example:\n\n```js\nvar nodeList = document.getElementsByClassName('my-class');\nvar filtered = Array.prototype.filter.call(nodeList, function (item) {\n  return item.innerText.indexOf('Item') !== -1;\n});\n```\n\n## Random utilities\n\n```js\n// $.proxy(fn, context);\nfn.bind(context);\n\n// $.parseJSON(string);\nJSON.parse(string);\n\n// $.trim(string);\nstring.trim();\n\n// $.type(obj);\nObject.prototype.toString.call(obj).replace(/^\\[object (.+)\\]$/, '$1').toLowerCase();\n```\n\n## Adding multiple `window.{onload, onerror}` events\n\n```js\n(function () {\n  function addWindowEvent(event, fn) {\n    var old = window[event];\n    if (typeof old !== 'function') {\n      window[event] = fn;\n      return;\n    }\n\n    window[event] = function () {\n      old.apply(window, arguments);\n      fn.apply(window, arguments);\n    }\n  }\n\n  window.addOnLoad = function (fn) {\n    addWindowEvent('onload', fn)\n  }\n\n  window.addOnError = function (fn) {\n    addWindowEvent('onerror', fn)\n  }\n})();\n```\n\n## XMLHttpRequest (XHR)\n\nDespite its name, `XMLHttpRequest` can be used to retrieve any type of data, not just XML, and it supports protocols other than HTTP (including file and ftp).\n\n**Getting data from the server:**\n\n```js\nvar xhr = new XMLHttpRequest();\nxhr.open('GET', '/url', true);\nxhr.onload = function () {\n  if (this.status === 200) {\n    console.log('success!');\n  } else {\n    console.log('failed', this.status);\n  }\n};\n\nxhr.send();\n```\n\n**Posting data back to the server:**\n\n```js\nvar xhrPost = new XMLHttpRequest();\nxhrPost.open('POST', '/url/post', true);\nxhrPost.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\nxhrPost.onload = function () {};\nxhrPost.send();\n```\n\n# Alternative libraries\n\n* AJAX: [Axios](https://github.com/mzabriskie/axios), [Superagent](https://github.com/visionmedia/superagent)\n* Animations: [Animate.css](https://github.com/daneden/animate.css), [Move.js](https://github.com/visionmedia/move.js)\n* Working with arrays, numbers, objects, strings, etc.: [Lodash](https://lodash.com/)\n\n# Credits and further resources\n\n* http://youmightnotneedjquery.com/\n* https://css-tricks.com/now-ever-might-not-need-jquery/\n* https://plainjs.com/javascript/\n* https://github.com/nefe/You-Dont-Need-jQuery\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffed%2Fdom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffed%2Fdom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffed%2Fdom/lists"}