HTML DOM Collection

Working with groups of HTML elements in JavaScript

📚 What are DOM Collections?

DOM collections are array-like objects that contain multiple HTML elements. They're returned by methods that find multiple elements at once.


// Get a collection of elements
let paragraphs = document.getElementsByTagName("p");
console.log(paragraphs.length);        // Number of paragraphs
console.log(paragraphs[0]);            // First paragraph
                                    

Output:

5
[object HTMLParagraphElement]

Types of Collections

📋

HTMLCollection

Live collection that updates automatically

getElementsByTagName()
getElementsByClassName()
children
📝

NodeList

Can be live or static collection

querySelectorAll()
childNodes
🔄

Live Collections

Update when DOM changes

getElementsByClassName()
children
📸

Static Collections

Snapshot that doesn't change

querySelectorAll()

🔹 Getting Collections

Different methods return different types of collections:

// HTMLCollection (live)
let divsByTag = document.getElementsByTagName("div");
let divsByClass = document.getElementsByClassName("container");
let formElements = document.forms;           // All forms
let imageElements = document.images;         // All images
let linkElements = document.links;           // All links

// NodeList (static)
let divsQuery = document.querySelectorAll("div");
let specificDivs = document.querySelectorAll("div.container");

// NodeList (live)
let allNodes = document.body.childNodes;     // Includes text nodes

// HTMLCollection (live) - only elements
let allElements = document.body.children;    // Only element nodes

console.log("Divs by tag:", divsByTag.length);
console.log("Divs by class:", divsByClass.length);
console.log("Divs by query:", divsQuery.length);
console.log("All forms:", formElements.length);

// Check collection type
console.log(divsByTag.constructor.name);     // HTMLCollection
console.log(divsQuery.constructor.name);     // NodeList

Output:

Divs by tag: 8
Divs by class: 3
Divs by query: 8
All forms: 1
HTMLCollection
NodeList

🔹 Accessing Collection Items

Different ways to access elements in collections:

let paragraphs = document.getElementsByTagName("p");

// Access by index (like arrays)
let firstP = paragraphs[0];
let secondP = paragraphs[1];
let lastP = paragraphs[paragraphs.length - 1];

// Access by name or id (HTMLCollection only)
let namedP = paragraphs.namedItem("myParagraph");  // By id or name attribute
let namedP2 = paragraphs["myParagraph"];           // Shorthand

// Using item() method
let thirdP = paragraphs.item(2);

// Check if collection is empty
if (paragraphs.length > 0) {
    console.log("Found", paragraphs.length, "paragraphs");
    console.log("First paragraph:", firstP.textContent);
} else {
    console.log("No paragraphs found");
}

// Safe access with bounds checking
function getElementSafely(collection, index) {
    if (index >= 0 && index < collection.length) {
        return collection[index];
    }
    return null;
}

let safeParagraph = getElementSafely(paragraphs, 10);
console.log(safeParagraph); // null if index out of bounds

Output:

Found 5 paragraphs
First paragraph: This is the first paragraph
null

🔹 Iterating Through Collections

Different ways to loop through DOM collections:

let buttons = document.getElementsByTagName("button");

// Method 1: Traditional for loop
for (let i = 0; i < buttons.length; i++) {
    console.log("Button", i + 1, ":", buttons[i].textContent);
    buttons[i].style.margin = "5px";
}

// Method 2: for...of loop (modern, recommended)
for (let button of buttons) {
    console.log("Button text:", button.textContent);
    button.addEventListener("click", function() {
        alert("Button clicked: " + this.textContent);
    });
}

// Method 3: Convert to array first (for more array methods)
let buttonArray = Array.from(buttons);
buttonArray.forEach(function(button, index) {
    console.log(`Button ${index}:`, button.textContent);
    button.classList.add("processed");
});

// Method 4: Using spread operator
let buttonArray2 = [...buttons];
buttonArray2.map(button => button.textContent)
           .forEach(text => console.log("Text:", text));

// Method 5: forEach (NodeList only, not HTMLCollection)
let divs = document.querySelectorAll("div");
divs.forEach(function(div, index) {
    console.log(`Div ${index}:`, div.className);
});

// Filter collections (convert to array first)
let visibleButtons = Array.from(buttons).filter(button => {
    return button.style.display !== "none";
});

console.log("Visible buttons:", visibleButtons.length);

Output:

Button 1 : Click Me
Button 2 : Submit
Button 3 : Cancel
Button text: Click Me
Button text: Submit
Button text: Cancel
Button 0: Click Me
Button 1: Submit
Button 2: Cancel
Text: Click Me
Text: Submit
Text: Cancel
Div 0: container
Div 1: sidebar
Visible buttons: 3

🔹 Live vs Static Collections

Understanding the difference between live and static collections:

// Live HTMLCollection - updates automatically
let liveCollection = document.getElementsByClassName("dynamic");
console.log("Initial count:", liveCollection.length); // 2

// Static NodeList - snapshot at time of creation
let staticCollection = document.querySelectorAll(".dynamic");
console.log("Static count:", staticCollection.length); // 2

// Add a new element with the class
let newDiv = document.createElement("div");
newDiv.className = "dynamic";
newDiv.textContent = "New dynamic element";
document.body.appendChild(newDiv);

// Check counts after adding element
console.log("Live count after adding:", liveCollection.length);   // 3 (updated!)
console.log("Static count after adding:", staticCollection.length); // 2 (unchanged)

// Remove an element
let firstDynamic = document.querySelector(".dynamic");
firstDynamic.remove();

console.log("Live count after removing:", liveCollection.length);   // 2 (updated!)
console.log("Static count after removing:", staticCollection.length); // 2 (unchanged)

// Be careful with live collections in loops!
let items = document.getElementsByClassName("item");

// WRONG - can cause infinite loop or skip elements
// for (let i = 0; i < items.length; i++) {
//     items[i].className = "processed"; // Removes from collection!
// }

// CORRECT - loop backwards or convert to array
for (let i = items.length - 1; i >= 0; i--) {
    items[i].className = "processed";
}

// OR convert to array first
Array.from(items).forEach(item => {
    item.className = "processed";
});

Output:

Initial count: 2
Static count: 2
Live count after adding: 3
Static count after adding: 2
Live count after removing: 2
Static count after removing: 2

🔹 Collection Methods and Properties

Useful methods and properties for working with collections:

let elements = document.getElementsByTagName("div");

// Basic properties
console.log("Length:", elements.length);
console.log("Constructor:", elements.constructor.name);

// HTMLCollection specific methods
console.log("Item at index 0:", elements.item(0));
console.log("Named item:", elements.namedItem("myDiv"));

// Check if collection has elements
function hasElements(collection) {
    return collection && collection.length > 0;
}

if (hasElements(elements)) {
    console.log("Collection has elements");
}

// Convert collection to array for more methods
let elementArray = Array.from(elements);

// Now you can use array methods
let filteredElements = elementArray.filter(el => el.className.includes("active"));
let elementTexts = elementArray.map(el => el.textContent);
let hasSpecificElement = elementArray.some(el => el.id === "special");

console.log("Filtered elements:", filteredElements.length);
console.log("Element texts:", elementTexts);
console.log("Has special element:", hasSpecificElement);

// Find specific elements in collection
function findInCollection(collection, predicate) {
    for (let i = 0; i < collection.length; i++) {
        if (predicate(collection[i])) {
            return collection[i];
        }
    }
    return null;
}

let redElement = findInCollection(elements, el => 
    el.style.color === "red"
);

// Count elements matching criteria
function countMatching(collection, predicate) {
    let count = 0;
    for (let element of collection) {
        if (predicate(element)) {
            count++;
        }
    }
    return count;
}

let hiddenCount = countMatching(elements, el => 
    el.style.display === "none"
);

Output:

Length: 8
Constructor: HTMLCollection
Item at index 0: [object HTMLDivElement]
Named item: [object HTMLDivElement]
Collection has elements
Filtered elements: 2
Element texts: ["Header", "Content", "Sidebar", "Footer"]
Has special element: true

🔹 Practical Examples

Real-world examples of working with DOM collections:

// Example 1: Style all elements of a type
function styleAllButtons() {
    let buttons = document.getElementsByTagName("button");
    for (let button of buttons) {
        button.style.padding = "10px 20px";
        button.style.borderRadius = "5px";
        button.style.border = "none";
        button.style.backgroundColor = "#007cba";
        button.style.color = "white";
        button.style.cursor = "pointer";
    }
}

// Example 2: Add event listeners to multiple elements
function addClickHandlers() {
    let links = document.getElementsByTagName("a");
    Array.from(links).forEach((link, index) => {
        link.addEventListener("click", function(event) {
            console.log(`Link ${index + 1} clicked:`, this.href);
            // Add analytics tracking here
        });
    });
}

// Example 3: Form validation for multiple inputs
function validateForm() {
    let inputs = document.querySelectorAll("input[required]");
    let isValid = true;
    
    inputs.forEach(input => {
        if (input.value.trim() === "") {
            input.style.borderColor = "red";
            isValid = false;
        } else {
            input.style.borderColor = "#ccc";
        }
    });
    
    return isValid;
}

// Example 4: Image gallery with lazy loading
function setupImageGallery() {
    let images = document.querySelectorAll("img[data-src]");
    
    let imageObserver = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                let img = entry.target;
                img.src = img.dataset.src;
                img.classList.remove("lazy");
                imageObserver.unobserve(img);
            }
        });
    });
    
    images.forEach(img => imageObserver.observe(img));
}

// Example 5: Table row highlighting
function setupTableHighlighting() {
    let rows = document.querySelectorAll("table tr");
    
    rows.forEach(row => {
        row.addEventListener("mouseenter", function() {
            this.style.backgroundColor = "#f0f0f0";
        });
        
        row.addEventListener("mouseleave", function() {
            this.style.backgroundColor = "";
        });
    });
}

// Example 6: Accordion functionality
function setupAccordion() {
    let headers = document.querySelectorAll(".accordion-header");
    
    headers.forEach(header => {
        header.addEventListener("click", function() {
            let content = this.nextElementSibling;
            let isOpen = content.style.display === "block";
            
            // Close all other accordions
            document.querySelectorAll(".accordion-content").forEach(c => {
                c.style.display = "none";
            });
            
            // Toggle current accordion
            content.style.display = isOpen ? "none" : "block";
        });
    });
}

Usage:

// Call these functions to set up functionality
styleAllButtons();
addClickHandlers();
setupImageGallery();
setupTableHighlighting();
setupAccordion();

🧠 Test Your Knowledge

Which collection type updates automatically when the DOM changes?