DOM (Document Object Model) Manipulations

TheTechnoCult
7 min readNov 22, 2023

--

The Document Object Model (DOM) is a programming interface for web documents. It represents the structure of a document as a tree-like model, where each node in the tree corresponds to a part of the document, such as elements, attributes, and text. In JavaScript, the DOM provides a way for scripts to dynamically access and manipulate the content, structure, and style of a document.

1. Select Elements in the DOM

// the most usable
const elementByClass = document.querySelector('.class-name') // Output: element.class-name
const elementsByClass = document.querySelectorAll('.class-name') // Output: [object NodeList], with text elements
const body = document.body
const html = document.documentElement

const elementById = document.getElementById('elem-id')

// Using getElementsByClassName
const elementsByClass = document.getElementsByClassName('class-name') // Output: [object HTMLCollection], only html elements
// Using getElementsByTagName
const elementsByTag = document.getElementsByTagName('div') // Output: [object HTMLCollection]

// Using querySelector
const elementByClass = document.querySelector('.class-name')
const elementByTag = document.querySelector('div')
const elementsByClass = document.querySelectorAll('.class-name') // Output: [object NodeList]

2. Styling Elements

  • Inline Styles
const element = document.querySelector('div')
element.style.color = 'red'
element.style.fontSize = '16px'
  • CSS Classes classList
// Assuming you have a CSS class defined:
// .highlight {
// background-color: yellow;
// font-weight: bold;
// }

const element = document.querySelector('.target-element')

element.classList.add('class-name')
element.classList.remove('class-name', 'bold')
element.classList.toggle('class-name')

const firstClass = element.classList.item(0) // Get the first class

element.classList.contains('class-name') // Checks if the element has a specific CSS class
  • Setting CSS Text / Inline Styles
const element = document.querySelector('.target-element')
element.style.cssText = 'color: blue; font-size: 18px;'
  • Getting Computed Styles
const element = document.querySelector('.target-element')
const computedStyles = window.getComputedStyle(element)

console.log(computedStyles.color) // Get the computed color
console.log(computedStyles.fontSize) // Get the computed font size

3. Traversing HTMLCollection and NodeList

Choose the method that best fits your coding style and requirements. If you need compatibility with older browsers that do not support forEach or for...of, the traditional for loop or converting the NodeList to an array using Array.from() are viable options. Using forEach or for...of often makes your code cleaner and more concise.

const elements = document.querySelectorAll('.class-name')

// 1. forEach
elements.forEach(element => {
console.log(element.textContent)
})

// 2. Using for...of Loop
for (const element of elements) {
console.log(element.textContent)
}

// 3. Array.from()
Array.from(elements).forEach(element => {
console.log(element.textContent)
})

// 4. Traditional for Loop
for (let i = 0; i < elements.length; i++) {
console.log(elements[i].textContent)
}

4. Create Elements

// Create a new paragraph element
const newParagraph = document.createElement('p')

// Set the 'class' attribute of the paragraph
newParagraph.setAttribute('class', 'class-name')

// Add content: use the textContent or innerHTML property to set the content of the element.
newParagraph.textContent = ' Text with spaces and breaks. '
newParagraph.innerText = 'Some cleaned text'
newParagraph.innerHTML = '<b>New</b> HTML content'
// (!) innerHTML parse string as HTML
// using innerHTML can introduce security risks, especially if the content comes from user input

// Append the paragraph to an existing div with id 'container'
const container = document.getElementById('container')
container.appendChild(newParagraph)

// Create a new div with a span inside
const newDiv = document.createElement('div')
const newSpan = document.createElement('span')
newSpan.textContent = 'This is a span inside a div.'
newDiv.appendChild(newSpan)

// Append the new div to the body of the document
document.body.appendChild(newDiv)
// Usage
const newParagraph = document.createElement('p')
newParagraph.setAttribute('class', 'class-name')
newParagraph.textContent = 'This is a new paragraph!'
const container = document.getElementById('container')
container.appendChild(newParagraph)

6. Insert Elements

const parent = document.getElementById('parent')
const newElement = document.createElement('div')

// Append the new element to the end of the parent's children
parent.appendChild(newElement)

const referenceElement = document.getElementById('reference')

// Insert the new element before the reference element
parent.insertBefore(newElement, referenceElement)

// Insert new HTML content after the end of the parent's content
parent.insertAdjacentHTML('beforeend', '<div>New Content</div>')

// Insert the new element after the end of the parent's content
parent.insertAdjacentElement('beforeend', newElement)

// Insert new text content after the end of the parent's content
parent.insertAdjacentText('beforeend', 'New Text Content')

7. Remove Elements

const elementToRemove = document.getElementById('toBeRemoved')

// Remove the element directly
elementToRemove.remove()

const parentElement = document.getElementById('parent')
const childElement = document.getElementById('child')

// Remove the child element
parentElement.removeChild(childElement)

// Remove the child element by accessing its parent
childElement.parentNode.removeChild(childElement)

const elementToRemove = document.getElementById('toBeRemoved')
const replacementContent = document.createTextNode('Replacement Text')

// Replace the element with new content
elementToRemove.replaceWith(replacementContent)

8. Attributes and Properties

Attributes are static, defined in HTML markup, and accessed using methods like getAttribute, while properties are dynamic, reflect the current state of elements, and are accessed directly in JavaScript.

<input id="myInput" value="Initial Value">
<script>
const inputElement = document.getElementById('input-id')

// Reading attribute and property
console.log(inputElement.getAttribute('value')) // Output: "Initial Value"
console.log(inputElement.value) // Output: "Initial Value"

// Changing attribute (does not affect property)
inputElement.setAttribute('value', 'New Value')
console.log(inputElement.getAttribute('value')) // Output: "New Value"
console.log(inputElement.value) // Output: "Initial Value" (property is unchanged)

// Changing property (does not affect attribute)
inputElement.value = 'Modified Value'
console.log(inputElement.getAttribute('value')) // Output: "New Value" (attribute is unchanged)
console.log(inputElement.value) // Output: "Modified Value"
</script>

9. How to Traverse and Move Around the DOM

  • Parent and Child Nodes
const element = document.getElementById('element')

// Move to the parent node
const parentNode = element.parentNode // Output: Node element
const grandParentNode = element.parentNode.parentNode // Output: Node element
// or less used
const parentElement = element.parentElement // Output: HTML element

// Access all child nodes
const childNodes = element.childNodes // Output: [object NodeList], with text nodes
const childElements = element.children // Output: [object HTMLCollection], only html elements

// Move to the first child node
const firstChildNode = element.firstChild // Output: Node element

// Move to the last child node
const lastChildNode = element.lastChild // Output: Node element
  • Siblings
const element = document.getElementById('element')

// Move to the next sibling
const nextSiblingNode = element.nextSibling // Output: Node element
const nextSiblingElement = element.nextElementSibling // Output: HTML element


// Move to the previous sibling
const previousSiblingNode = element.previousSibling // Output: Node element
const previousSiblingElement = element.previousElementSibling // Output: HTML element
  • Descendants
const element = document.getElementById('element')

// Move to the first descendant with a specific tag name
const descendantElement = element.querySelector('p')

// Move to all descendants with a specific class
const descendantsWithClass = element.getElementsByClassName('some-class')
  • Closest Ancestor
const element = document.getElementById('element')

// Move to the closest ancestor with a specific class
const closestAncestor = element.closest('.ancestorClass')
  • Traversing Up and Down
const element = document.getElementById('element')

// Traverse up to the closest ancestor with a specific class
const ancestorWithClass = element.closest('.ancestorClass')

// Traverse down to find the first descendant with a specific tag name
const descendantWithTag = element.querySelector('p')

10. Get Size of the Element

// Using getBoundingClientRect() Method:
// returns the size and position of an element relative to the viewport
const element = document.getElementById('element')
const rect = element.getBoundingClientRect()

console.log('Width:', rect.width)
console.log('Height:', rect.height)
// Using offsetWidth and offsetHeight Properties:
// provide the total width and height of an element,
// including padding and border but excluding margin
console.log('Offset Width:', element.offsetWidth)
console.log('Offset Height:', element.offsetHeight)
// Using clientWidth and clientHeight Properties:
// provide the inner width and height of an element,
// excluding padding and border
console.log('Client Width:', element.clientWidth)
console.log('Client Height:', element.clientHeight)
// Using scrollWidth and scrollHeight Properties:
// provide the total width and height of an element's content,
// including overflow
console.log('Scroll Width:', element.scrollWidth)
console.log('Scroll Height:', element.scrollHeight)

11. Scrolling

// Window Scroll
// To get the scroll values of the entire window
console.log('Window Scroll X:', window.scrollX)
console.log('Window Scroll Y:', window.scrollY)

// Element Scroll
// To get the scroll values of a specific element
const element = document.getElementById('element')
console.log('Element Scroll Left:', element.scrollLeft)
console.log('Element Scroll Top:', element.scrollTop)

The scrollX and scrollY properties are supported in most modern browsers, but for better compatibility, you can use window.pageXOffset and window.pageYOffset as well.

12. Events

Event propagation includes three phases: (1) Capturing > (2) Target > (3) Bubbling.

|------------------------|  <- Outermost Ancestor (Capturing Phase)
| |---------------------| <- Ancestor (Capturing Phase)
| | |------------------| <- Ancestor (Capturing Phase)
| | | Target Element | <- Target Phase
| | |------------------| <- Ancestor (Bubbling Phase)
| |---------------------| <- Ancestor (Bubbling Phase)
|------------------------| <- Outermost Ancestor (Bubbling Phase)

Mouse Events: click, dblclick, mousedown, mouseup, mousemove, mouseenter / mouseover, mouseleave / mouseout.

Keyboard Events: keydown, keyup, keypress.

Form Events: submit, reset, focus, blur, change, input, select.

Window Events: load, unload, resize, scroll.

Focus Events: focus, blur.

Clipboard Events: cut, copy, paste.

Media Events: play, pause, ended.

Drag and Drop Events: dragstart, dragenter, dragover, dragleave, drop, dragend.

  • Mobile Events:

Touch Events: touchstart, touchmove, touchend, touchcancel.

Gesture Events: gesturestart, gesturechange, gestureend.

Orientation Event: orientationchange.

Device Motion and Orientation Events: devicemotion, deviceorientation.

Pointer Events: pointerdown, pointermove, pointerup, pointercancel.

13. Event handling

  • Attaching / Removing Event Listeners
const button = document.getElementById('button-id')

function handleClick(event) {
// Code to be executed when the button is clicked
console.log('Button clicked!')
console.log('Event type:', event.type)
}
// Add event listener
button.addEventListener('click', handleClick)
// Remove event listener
button.removeEventListener('click', handleClick)
  • Event Propagation

Events in the DOM can propagate through different phases: (1) capturing phase, (2) target phase, and (3) bubbling phase. You can control the phase during which the event listener should be triggered using the addEventListener method's optional third parameter, which specifies whether to use the capturing phase (true) or the bubbling phase (false, default).

const button = document.getElementById('myButton')

button.addEventListener('click', function() {
console.log('Button clicked!')
}, true) // Use capturing phase
// if true, click by element inits: document listener -> container -> element
// if false, click inits: element listener -> container -> document

You can stop the propagation of an event using the stopPropagation method on the event object. This prevents the event from continuing through either the capturing or bubbling phases.

const container = document.getElementById('container')

container.addEventListener('click', function(event) {
console.log('Container clicked!')
event.stopPropagation() // Stop further propagation
});
document.addEventListener('click', function() {
console.log('Document clicked!') // This won't be executed if stopPropagation is called
})
  • preventDefault method is used to prevent the default behavior associated with an event.
const link = document.getElementById('myLink')

link.addEventListener('click', function(event) {
// Prevent the default link behavior (e.g., navigating to a new page)
event.preventDefault()
// Custom behavior
console.log('Link clicked, but default action prevented!')
})
  • Event delegation is a technique in JavaScript where a single event listener is used to manage multiple elements by taking advantage of event bubbling. Instead of attaching an event listener to each individual element, you attach it to a common ancestor and handle events as they bubble up from the target element. This is particularly useful when dealing with a large number of dynamically created elements.
/* <ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<!-- More dynamically added items will also be handled -->
</ul>
*/

// Attach a single event listener to the common ancestor
const list = document.getElementById('myList');
list.addEventListener('click', function(event) {
// Check if the clicked element is an <li> inside the #myList
if (event.target.tagName === 'LI') {
// Access the text content of the clicked <li> element
const clickedItem = event.target.textContent
console.log(`Clicked on: ${clickedItem}`)
}
})

--

--

No responses yet