Session 2.5 – DOM Manipulation

Module 2: JavaScript & AJAX | Duration: 1 hr

Learning Objectives

By the end of this session, students will be able to:

  • Understand the Document Object Model (DOM) structure
  • Select and access HTML elements using various methods
  • Modify element content, attributes, and styles
  • Create, append, and remove elements dynamically
  • Navigate the DOM tree structure

Introduction to DOM

The Document Object Model (DOM) is a programming interface for HTML documents. It represents the page structure as a tree of objects, allowing JavaScript to access and manipulate the content, structure, and style of a web page.

What is the DOM?

The DOM is not part of JavaScript itself but is a Web API provided by browsers. It creates a logical tree structure from your HTML document, where each element, attribute, and piece of text becomes a node that JavaScript can interact with.

DOM Structure

The DOM represents an HTML document as a tree structure, where each element is a node.

document
  └─ html
      ├─ head
      │   ├─ title
      │   └─ meta
      └─ body
          ├─ header
          │   └─ h1
          ├─ main
          │   ├─ section
          │   │   ├─ h2
          │   │   └─ p
          │   └─ section
          └─ footer
// HTML Example:
// <div id="container">
//   <h1 class="title">Hello World</h1>
//   <p class="text">This is a paragraph.</p>
// </div>

// Accessing the document object
console.log(document);              // The entire document
console.log(document.documentElement); // <html> element
console.log(document.head);         // <head> element
console.log(document.body);         // <body> element
console.log(document.title);        // Page title

// Document information
console.log(document.URL);          // Current URL
console.log(document.domain);       // Domain name
console.log(document.lastModified); // Last modification date

Selecting Elements

Classic Selection Methods

// getElementById - returns single element
let element = document.getElementById("myId");
console.log(element);

// getElementsByClassName - returns HTMLCollection (array-like)
let elements = document.getElementsByClassName("myClass");
console.log(elements[0]);  // Access first element

// getElementsByTagName - returns HTMLCollection
let paragraphs = document.getElementsByTagName("p");
for (let p of paragraphs) {
    console.log(p.textContent);
}

// getElementsByName - returns NodeList
let radioButtons = document.getElementsByName("gender");
console.log(radioButtons);

Modern Selection Methods (Recommended)

// querySelector - returns first matching element
let firstParagraph = document.querySelector("p");
let elementById = document.querySelector("#myId");
let elementByClass = document.querySelector(".myClass");
let complexSelector = document.querySelector("div.container > p.text");

console.log(firstParagraph);

// querySelectorAll - returns NodeList (array-like)
let allParagraphs = document.querySelectorAll("p");
let allDivs = document.querySelectorAll("div");
let specificElements = document.querySelectorAll("div.active, p.highlight");

// Convert to array for array methods
let paragraphArray = Array.from(allParagraphs);
paragraphArray.forEach(p => console.log(p.textContent));

// Or use spread operator
[...allParagraphs].map(p => p.textContent);

// querySelectorAll examples
document.querySelectorAll("div > p");        // Direct children
document.querySelectorAll("div p");          // All descendants
document.querySelectorAll("p:first-child");  // First child p elements
document.querySelectorAll("p:nth-child(2)"); // Second child p elements
document.querySelectorAll("a[target]");      // Links with target attribute
document.querySelectorAll("input[type='text']"); // Text inputs

Modifying Elements

Modifying Content

let element = document.getElementById("myElement");

// textContent - gets/sets text content (no HTML)
console.log(element.textContent);
element.textContent = "New text content";

// innerHTML - gets/sets HTML content (including tags)
console.log(element.innerHTML);
element.innerHTML = "<strong>Bold text</strong>";

// innerText - similar to textContent but respects styling
console.log(element.innerText);
element.innerText = "Visible text only";

// Differences:
let div = document.querySelector("div");
// If div contains: <p>Hello <span style="display:none">World</span></p>
console.log(div.textContent); // "Hello World"
console.log(div.innerText);   // "Hello" (World is hidden)
console.log(div.innerHTML);   // "<p>Hello <span style="display:none">World</span></p>"

Modifying Attributes

let link = document.querySelector("a");
let image = document.querySelector("img");

// getAttribute() - get attribute value
let href = link.getAttribute("href");
let src = image.getAttribute("src");

// setAttribute() - set attribute value
link.setAttribute("href", "https://example.com");
link.setAttribute("target", "_blank");
image.setAttribute("src", "newimage.jpg");
image.setAttribute("alt", "New image");

// hasAttribute() - check if attribute exists
if (link.hasAttribute("target")) {
    console.log("Link has target attribute");
}

// removeAttribute() - remove attribute
link.removeAttribute("target");

// Direct property access (preferred for standard attributes)
link.href = "https://example.com";
link.id = "myLink";
link.className = "nav-link active";
image.src = "image.jpg";
image.alt = "Description";

// data-* attributes
let element = document.querySelector("div");
element.setAttribute("data-user-id", "123");
element.setAttribute("data-role", "admin");

// Access via dataset (camelCase for multi-word attributes)
console.log(element.dataset.userId);  // "123"
console.log(element.dataset.role);    // "admin"
element.dataset.status = "active";

Modifying Styles

let element = document.getElementById("myElement");

// Inline styles via style property
element.style.color = "blue";
element.style.backgroundColor = "yellow";
element.style.fontSize = "20px";
element.style.padding = "10px";
element.style.border = "2px solid black";

// CSS properties with hyphens use camelCase
element.style.marginTop = "10px";
element.style.borderRadius = "5px";

// Get computed styles (including CSS file styles)
let computedStyle = window.getComputedStyle(element);
console.log(computedStyle.color);
console.log(computedStyle.fontSize);

// cssText - set multiple styles at once
element.style.cssText = "color: red; background: yellow; padding: 10px;";

// Preferred: Use classes instead of inline styles
element.classList.add("highlight");
element.classList.remove("hidden");
element.classList.toggle("active");
element.classList.replace("old-class", "new-class");

// Check if class exists
if (element.classList.contains("active")) {
    console.log("Element is active");
}

// className (overwrites all classes)
element.className = "btn btn-primary";

// classList (better - preserves other classes)
element.classList.add("btn", "btn-primary", "btn-lg");

Creating and Removing Elements

Creating New Elements

// createElement - create new element
let newDiv = document.createElement("div");
let newParagraph = document.createElement("p");
let newButton = document.createElement("button");

// Set content and attributes
newDiv.textContent = "This is a new div";
newDiv.id = "newDiv";
newDiv.className = "container";

newParagraph.innerHTML = "This is a <strong>new</strong> paragraph";

newButton.textContent = "Click Me";
newButton.onclick = () => alert("Clicked!");

// createTextNode - create text node
let textNode = document.createTextNode("Hello, World!");

// Append to document
document.body.appendChild(newDiv);
newDiv.appendChild(newParagraph);
newDiv.appendChild(newButton);

// insertBefore - insert before specific element
let referenceElement = document.getElementById("reference");
let parent = referenceElement.parentNode;
parent.insertBefore(newDiv, referenceElement);

// insertAdjacentHTML - insert HTML at specific position
element.insertAdjacentHTML("beforebegin", "<p>Before</p>");
element.insertAdjacentHTML("afterbegin", "<p>First child</p>");
element.insertAdjacentHTML("beforeend", "<p>Last child</p>");
element.insertAdjacentHTML("afterend", "<p>After</p>");

// insertAdjacentElement - insert element at specific position
let newElement = document.createElement("div");
element.insertAdjacentElement("afterend", newElement);

// append() and prepend() - modern methods (can take multiple nodes/strings)
parent.append(newDiv, "Some text", newParagraph);
parent.prepend(newButton);

Removing Elements

let element = document.getElementById("myElement");

// remove() - modern way
element.remove();

// removeChild() - older way
let parent = element.parentNode;
parent.removeChild(element);

// Remove all children
let container = document.getElementById("container");
while (container.firstChild) {
    container.removeChild(container.firstChild);
}

// Or simply:
container.innerHTML = "";

// Replace element
let oldElement = document.getElementById("old");
let newElement = document.createElement("div");
newElement.textContent = "New element";
oldElement.replaceWith(newElement);

// Or older way:
let parent = oldElement.parentNode;
parent.replaceChild(newElement, oldElement);

Cloning Elements

let original = document.getElementById("original");

// Clone without children (shallow clone)
let shallowClone = original.cloneNode(false);

// Clone with all children (deep clone)
let deepClone = original.cloneNode(true);

// Append clones
document.body.appendChild(shallowClone);
document.body.appendChild(deepClone);

// Useful for templates
let template = document.getElementById("item-template");
let newItem = template.cloneNode(true);
newItem.id = "item-" + Date.now();
newItem.querySelector(".item-name").textContent = "New Item";
document.getElementById("item-list").appendChild(newItem);

DOM Traversal

Navigate between elements in the DOM tree using parent, child, and sibling relationships.

let element = document.getElementById("myElement");

// Parent relationships
console.log(element.parentNode);        // Parent node
console.log(element.parentElement);     // Parent element
console.log(element.closest(".container")); // Closest ancestor with class

// Child relationships
console.log(element.childNodes);        // All child nodes (including text)
console.log(element.children);          // Only element children
console.log(element.firstChild);        // First child node
console.log(element.firstElementChild); // First child element
console.log(element.lastChild);         // Last child node
console.log(element.lastElementChild);  // Last child element
console.log(element.hasChildNodes());   // Check if has children
console.log(element.childElementCount); // Number of child elements

// Sibling relationships
console.log(element.nextSibling);           // Next node
console.log(element.nextElementSibling);    // Next element
console.log(element.previousSibling);       // Previous node
console.log(element.previousElementSibling);// Previous element

// Practical traversal example
function getAllSiblings(element) {
    let siblings = [];
    let sibling = element.parentNode.firstChild;

    while (sibling) {
        if (sibling.nodeType === 1 && sibling !== element) {
            siblings.push(sibling);
        }
        sibling = sibling.nextSibling;
    }

    return siblings;
}

// Get all parents up to body
function getParents(element) {
    let parents = [];
    let parent = element.parentElement;

    while (parent) {
        parents.push(parent);
        parent = parent.parentElement;
    }

    return parents;
}

Practical Examples

Example 1: Dynamic Table Generator

// Generate HTML table dynamically
function generateTable(rows, cols) {
    let table = document.createElement("table");
    table.className = "table table-bordered";

    // Create thead
    let thead = document.createElement("thead");
    let headerRow = document.createElement("tr");

    for (let i = 0; i < cols; i++) {
        let th = document.createElement("th");
        th.textContent = `Column ${i + 1}`;
        headerRow.appendChild(th);
    }

    thead.appendChild(headerRow);
    table.appendChild(thead);

    // Create tbody
    let tbody = document.createElement("tbody");

    for (let i = 0; i < rows; i++) {
        let row = document.createElement("tr");

        for (let j = 0; j < cols; j++) {
            let cell = document.createElement("td");
            cell.textContent = `Cell ${i + 1}-${j + 1}`;
            row.appendChild(cell);
        }

        tbody.appendChild(row);
    }

    table.appendChild(tbody);
    return table;
}

// Usage
document.getElementById("generateTableBtn").addEventListener("click", () => {
    let container = document.getElementById("tableContainer");
    container.innerHTML = "";  // Clear previous table
    let table = generateTable(5, 4);
    container.appendChild(table);
});

Example 2: Image Gallery with Modal

// HTML:
// <div id="gallery"></div>
// <div id="modal" class="modal">
//   <span class="close">×</span>
//   <img id="modalImage" src="">
// </div>

let images = [
    "image1.jpg",
    "image2.jpg",
    "image3.jpg",
    "image4.jpg"
];

let gallery = document.getElementById("gallery");

// Create gallery items
images.forEach((src, index) => {
    let imgContainer = document.createElement("div");
    imgContainer.className = "gallery-item";

    let img = document.createElement("img");
    img.src = src;
    img.alt = `Image ${index + 1}`;
    img.style.width = "200px";
    img.style.margin = "10px";
    img.style.cursor = "pointer";

    // Click to open modal
    img.addEventListener("click", () => {
        document.getElementById("modal").style.display = "block";
        document.getElementById("modalImage").src = src;
    });

    imgContainer.appendChild(img);
    gallery.appendChild(imgContainer);
});

// Close modal
document.querySelector(".close").addEventListener("click", () => {
    document.getElementById("modal").style.display = "none";
});

Example 3: Accordion Component

// Create an accordion component
function createAccordion(data) {
    let accordion = document.createElement("div");
    accordion.className = "accordion";

    data.forEach((item, index) => {
        // Create accordion item
        let accordionItem = document.createElement("div");
        accordionItem.className = "accordion-item";

        // Create header
        let header = document.createElement("div");
        header.className = "accordion-header";
        header.textContent = item.title;
        header.style.padding = "10px";
        header.style.backgroundColor = "#f1f1f1";
        header.style.cursor = "pointer";
        header.style.borderBottom = "1px solid #ddd";

        // Create content
        let content = document.createElement("div");
        content.className = "accordion-content";
        content.textContent = item.content;
        content.style.padding = "10px";
        content.style.display = "none";

        // Toggle on header click
        header.addEventListener("click", () => {
            let isOpen = content.style.display === "block";
            content.style.display = isOpen ? "none" : "block";
            header.style.backgroundColor = isOpen ? "#f1f1f1" : "#ddd";
        });

        accordionItem.appendChild(header);
        accordionItem.appendChild(content);
        accordion.appendChild(accordionItem);
    });

    return accordion;
}

// Usage
let accordionData = [
    { title: "Section 1", content: "Content for section 1" },
    { title: "Section 2", content: "Content for section 2" },
    { title: "Section 3", content: "Content for section 3" }
];

let myAccordion = createAccordion(accordionData);
document.body.appendChild(myAccordion);

Example 4: Filter List

// HTML:
// <input type="text" id="searchInput" placeholder="Search...">
// <ul id="itemList">
//   <li>Apple</li>
//   <li>Banana</li>
//   <li>Orange</li>
// </ul>

let searchInput = document.getElementById("searchInput");
let itemList = document.getElementById("itemList");

searchInput.addEventListener("input", (e) => {
    let searchTerm = e.target.value.toLowerCase();
    let items = itemList.getElementsByTagName("li");

    Array.from(items).forEach(item => {
        let text = item.textContent.toLowerCase();

        if (text.includes(searchTerm)) {
            item.style.display = "";
        } else {
            item.style.display = "none";
        }
    });
});

Session Summary

Key Points
  • The DOM represents HTML as a tree structure of nodes
  • querySelector() and querySelectorAll() are modern, flexible selection methods
  • textContent for text, innerHTML for HTML content, and style for CSS properties
  • classList provides a clean way to manage CSS classes
  • createElement(), appendChild(), and remove() for dynamic content
  • DOM traversal methods navigate parent-child-sibling relationships
  • cloneNode() duplicates elements for templates and reuse
  • Always manipulate the DOM efficiently to avoid performance issues
Next Session Preview

In the next session, we will explore Client-Side Form Validation, learning how to validate user input before submission using JavaScript.