javascript
exercises
exercises.js⚡javascript
/**
* ============================================================
* 10.1 DOM BASICS - EXERCISES
* ============================================================
*
* These exercises are designed for browser environment.
* Create an HTML file with the provided structure and include this script.
*
* Sample HTML structure for testing:
* <!DOCTYPE html>
* <html>
* <head><title>DOM Exercises</title></head>
* <body>
* <div id="container">
* <header id="header" class="main-header">
* <h1>Welcome</h1>
* <nav id="nav" class="navigation">
* <ul id="nav-list">
* <li class="nav-item active"><a href="#home">Home</a></li>
* <li class="nav-item"><a href="#about">About</a></li>
* <li class="nav-item"><a href="#contact">Contact</a></li>
* </ul>
* </nav>
* </header>
* <main id="main">
* <article class="post" data-id="1" data-category="tech">
* <h2>First Post</h2>
* <p class="content">Hello World</p>
* </article>
* <article class="post" data-id="2" data-category="news">
* <h2>Second Post</h2>
* <p class="content">Another post</p>
* </article>
* </main>
* <footer id="footer">
* <p>© 2024</p>
* </footer>
* </div>
* <script src="exercises.js"></script>
* </body>
* </html>
*/
/**
* EXERCISE 1: Select by ID
*
* Create a function 'getHeader' that:
* - Returns the element with id "header"
*/
// TODO: Implement getHeader
function getHeader() {
// Your code here
}
// Test (in browser):
// console.log('Exercise 1:', getHeader());
/*
* SOLUTION 1:
*
* function getHeader() {
* return document.getElementById('header');
* }
*/
/**
* EXERCISE 2: Select by Class
*
* Create a function 'getNavItems' that:
* - Returns an array of all elements with class "nav-item"
*/
// TODO: Implement getNavItems
function getNavItems() {
// Your code here
}
// Test:
// console.log('Exercise 2:', getNavItems());
/*
* SOLUTION 2:
*
* function getNavItems() {
* return Array.from(document.querySelectorAll('.nav-item'));
* }
*/
/**
* EXERCISE 3: Select with Complex Selector
*
* Create a function 'getPostTitles' that:
* - Returns an array of all h2 elements inside .post articles
* - Returns the text content of each title
*/
// TODO: Implement getPostTitles
function getPostTitles() {
// Your code here
}
// Test:
// console.log('Exercise 3:', getPostTitles()); // ["First Post", "Second Post"]
/*
* SOLUTION 3:
*
* function getPostTitles() {
* const titles = document.querySelectorAll('.post h2');
* return Array.from(titles).map(h2 => h2.textContent);
* }
*/
/**
* EXERCISE 4: Scoped Query
*
* Create a function 'getLinksInNav' that:
* - Takes a nav element as parameter
* - Returns all anchor tags within that nav
*/
// TODO: Implement getLinksInNav
function getLinksInNav(nav) {
// Your code here
}
// Test:
// const nav = document.getElementById('nav');
// console.log('Exercise 4:', getLinksInNav(nav));
/*
* SOLUTION 4:
*
* function getLinksInNav(nav) {
* return Array.from(nav.querySelectorAll('a'));
* }
*/
/**
* EXERCISE 5: Parent Traversal
*
* Create a function 'getPostContainer' that:
* - Takes any element inside a .post
* - Returns the closest .post ancestor
*/
// TODO: Implement getPostContainer
function getPostContainer(element) {
// Your code here
}
// Test:
// const paragraph = document.querySelector('.content');
// console.log('Exercise 5:', getPostContainer(paragraph));
/*
* SOLUTION 5:
*
* function getPostContainer(element) {
* return element.closest('.post');
* }
*/
/**
* EXERCISE 6: Child Navigation
*
* Create a function 'getFirstAndLastNavItem' that:
* - Returns object { first, last } with first and last nav items
*/
// TODO: Implement getFirstAndLastNavItem
function getFirstAndLastNavItem() {
// Your code here
}
// Test:
// console.log('Exercise 6:', getFirstAndLastNavItem());
/*
* SOLUTION 6:
*
* function getFirstAndLastNavItem() {
* const navList = document.getElementById('nav-list');
* return {
* first: navList.firstElementChild,
* last: navList.lastElementChild
* };
* }
*/
/**
* EXERCISE 7: Sibling Navigation
*
* Create a function 'getSiblings' that:
* - Takes an element
* - Returns an array of all its sibling elements (not including itself)
*/
// TODO: Implement getSiblings
function getSiblings(element) {
// Your code here
}
// Test:
// const middle = document.querySelectorAll('.nav-item')[1];
// console.log('Exercise 7:', getSiblings(middle));
/*
* SOLUTION 7:
*
* function getSiblings(element) {
* return Array.from(element.parentElement.children)
* .filter(child => child !== element);
* }
*/
/**
* EXERCISE 8: Get Text Content
*
* Create a function 'getAllText' that:
* - Takes an element
* - Returns all text content (recursively) as a string
*/
// TODO: Implement getAllText
function getAllText(element) {
// Your code here
}
// Test:
// console.log('Exercise 8:', getAllText(document.getElementById('header')));
/*
* SOLUTION 8:
*
* function getAllText(element) {
* return element.textContent.trim();
* }
*/
/**
* EXERCISE 9: Class Manipulation
*
* Create a function 'toggleActiveClass' that:
* - Takes an element
* - Toggles the 'active' class on it
* - Returns true if class was added, false if removed
*/
// TODO: Implement toggleActiveClass
function toggleActiveClass(element) {
// Your code here
}
// Test:
// const item = document.querySelector('.nav-item');
// console.log('Exercise 9:', toggleActiveClass(item));
/*
* SOLUTION 9:
*
* function toggleActiveClass(element) {
* const wasActive = element.classList.contains('active');
* element.classList.toggle('active');
* return !wasActive;
* }
*/
/**
* EXERCISE 10: Multiple Class Operations
*
* Create a function 'setClasses' that:
* - Takes an element and object { add: [...], remove: [...] }
* - Adds and removes the specified classes
*/
// TODO: Implement setClasses
function setClasses(element, { add = [], remove = [] }) {
// Your code here
}
// Test:
// const post = document.querySelector('.post');
// setClasses(post, { add: ['featured', 'highlighted'], remove: ['draft'] });
// console.log('Exercise 10:', post.classList);
/*
* SOLUTION 10:
*
* function setClasses(element, { add = [], remove = [] }) {
* if (add.length) element.classList.add(...add);
* if (remove.length) element.classList.remove(...remove);
* }
*/
/**
* EXERCISE 11: Get Data Attributes
*
* Create a function 'getPostData' that:
* - Takes a post element
* - Returns object with all data attributes { id, category, ... }
*/
// TODO: Implement getPostData
function getPostData(post) {
// Your code here
}
// Test:
// const post = document.querySelector('.post');
// console.log('Exercise 11:', getPostData(post)); // { id: "1", category: "tech" }
/*
* SOLUTION 11:
*
* function getPostData(post) {
* return { ...post.dataset };
* }
*/
/**
* EXERCISE 12: Find by Attribute
*
* Create a function 'findPostsByCategory' that:
* - Takes a category string
* - Returns all posts with that data-category value
*/
// TODO: Implement findPostsByCategory
function findPostsByCategory(category) {
// Your code here
}
// Test:
// console.log('Exercise 12:', findPostsByCategory('tech'));
/*
* SOLUTION 12:
*
* function findPostsByCategory(category) {
* return Array.from(
* document.querySelectorAll(`[data-category="${category}"]`)
* );
* }
*/
/**
* EXERCISE 13: Check Element State
*
* Create a function 'isActiveNavItem' that:
* - Takes a nav item element
* - Returns true if it has the 'active' class
*/
// TODO: Implement isActiveNavItem
function isActiveNavItem(item) {
// Your code here
}
// Test:
// const items = document.querySelectorAll('.nav-item');
// console.log('Exercise 13:', Array.from(items).map(isActiveNavItem));
/*
* SOLUTION 13:
*
* function isActiveNavItem(item) {
* return item.classList.contains('active');
* }
*/
/**
* EXERCISE 14: Count Elements
*
* Create a function 'countElements' that:
* - Takes a selector
* - Returns the count of matching elements
*/
// TODO: Implement countElements
function countElements(selector) {
// Your code here
}
// Test:
// console.log('Exercise 14:', countElements('.nav-item')); // 3
// console.log('Exercise 14:', countElements('.post')); // 2
/*
* SOLUTION 14:
*
* function countElements(selector) {
* return document.querySelectorAll(selector).length;
* }
*/
/**
* EXERCISE 15: DOM Tree Walker
*
* Create a function 'getElementPath' that:
* - Takes an element
* - Returns the path from document to that element
* - Path format: "html > body > div#container > main#main > article"
*/
// TODO: Implement getElementPath
function getElementPath(element) {
// Your code here
}
// Test:
// const post = document.querySelector('.post');
// console.log('Exercise 15:', getElementPath(post));
/*
* SOLUTION 15:
*
* function getElementPath(element) {
* const path = [];
* let current = element;
*
* while (current && current !== document) {
* let selector = current.tagName.toLowerCase();
* if (current.id) {
* selector += `#${current.id}`;
* }
* path.unshift(selector);
* current = current.parentElement;
* }
*
* return path.join(' > ');
* }
*/
// ============================================================
// ADVANCED EXERCISES
// ============================================================
/**
* EXERCISE 16: Query Builder
*
* Create a class 'DOMQuery' that:
* - Constructor takes a selector or element
* - Has method 'find(selector)' returning new DOMQuery scoped to children
* - Has method 'first()' returning first element
* - Has method 'last()' returning last element
* - Has method 'each(callback)' to iterate elements
*/
// TODO: Implement DOMQuery
class DOMQuery {
// Your code here
}
// Test:
// const q = new DOMQuery('#nav-list');
// q.find('.nav-item').each((item, i) => console.log(i, item.textContent));
/*
* SOLUTION 16:
*
* class DOMQuery {
* constructor(selectorOrElements) {
* if (typeof selectorOrElements === 'string') {
* this.elements = Array.from(document.querySelectorAll(selectorOrElements));
* } else if (selectorOrElements instanceof Element) {
* this.elements = [selectorOrElements];
* } else if (Array.isArray(selectorOrElements)) {
* this.elements = selectorOrElements;
* } else {
* this.elements = [];
* }
* }
*
* find(selector) {
* const found = this.elements.flatMap(el =>
* Array.from(el.querySelectorAll(selector))
* );
* return new DOMQuery(found);
* }
*
* first() {
* return this.elements[0] || null;
* }
*
* last() {
* return this.elements[this.elements.length - 1] || null;
* }
*
* each(callback) {
* this.elements.forEach(callback);
* return this;
* }
* }
*/
/**
* EXERCISE 17: Element Matcher
*
* Create a function 'matchElements' that:
* - Takes an array of elements and a filter object
* - Filter can have: { hasClass, hasAttribute, matchesSelector }
* - Returns elements matching all conditions
*/
// TODO: Implement matchElements
function matchElements(elements, filter) {
// Your code here
}
// Test:
// const items = Array.from(document.querySelectorAll('.nav-item'));
// console.log('Exercise 17:', matchElements(items, {
// hasClass: 'active',
// matchesSelector: 'li'
// }));
/*
* SOLUTION 17:
*
* function matchElements(elements, filter) {
* return elements.filter(el => {
* if (filter.hasClass && !el.classList.contains(filter.hasClass)) {
* return false;
* }
* if (filter.hasAttribute && !el.hasAttribute(filter.hasAttribute)) {
* return false;
* }
* if (filter.matchesSelector && !el.matches(filter.matchesSelector)) {
* return false;
* }
* return true;
* });
* }
*/
/**
* EXERCISE 18: DOM Diff
*
* Create a function 'compareElements' that:
* - Takes two elements
* - Returns object describing differences in tagName, classes, attributes
*/
// TODO: Implement compareElements
function compareElements(el1, el2) {
// Your code here
}
// Test:
// const posts = document.querySelectorAll('.post');
// console.log('Exercise 18:', compareElements(posts[0], posts[1]));
/*
* SOLUTION 18:
*
* function compareElements(el1, el2) {
* const diff = {
* sameTag: el1.tagName === el2.tagName,
* classes: {
* onlyInFirst: [...el1.classList].filter(c => !el2.classList.contains(c)),
* onlyInSecond: [...el2.classList].filter(c => !el1.classList.contains(c)),
* shared: [...el1.classList].filter(c => el2.classList.contains(c))
* },
* attributes: {
* different: []
* }
* };
*
* // Compare data attributes
* const keys1 = Object.keys(el1.dataset);
* const keys2 = Object.keys(el2.dataset);
* const allKeys = new Set([...keys1, ...keys2]);
*
* allKeys.forEach(key => {
* if (el1.dataset[key] !== el2.dataset[key]) {
* diff.attributes.different.push({
* key: `data-${key}`,
* first: el1.dataset[key],
* second: el2.dataset[key]
* });
* }
* });
*
* return diff;
* }
*/
/**
* EXERCISE 19: Live Collection Handler
*
* Create a function 'observeCollection' that:
* - Takes a live HTMLCollection and a callback
* - Calls callback when collection length changes
* - Uses polling to check for changes
* - Returns a stop function
*/
// TODO: Implement observeCollection
function observeCollection(collection, callback, interval = 100) {
// Your code here
}
// Test:
// const buttons = document.getElementsByClassName('btn');
// const stop = observeCollection(buttons, (oldLen, newLen) => {
// console.log(`Collection changed: ${oldLen} -> ${newLen}`);
// });
// // Later: stop();
/*
* SOLUTION 19:
*
* function observeCollection(collection, callback, interval = 100) {
* let lastLength = collection.length;
*
* const intervalId = setInterval(() => {
* const currentLength = collection.length;
* if (currentLength !== lastLength) {
* callback(lastLength, currentLength);
* lastLength = currentLength;
* }
* }, interval);
*
* return function stop() {
* clearInterval(intervalId);
* };
* }
*/
/**
* EXERCISE 20: Selector Performance Tester
*
* Create a function 'benchmarkSelectors' that:
* - Takes an array of selectors
* - Measures time to execute each 1000 times
* - Returns results sorted by speed
*/
// TODO: Implement benchmarkSelectors
function benchmarkSelectors(selectors) {
// Your code here
}
// Test:
// console.log('Exercise 20:', benchmarkSelectors([
// '#header',
// '.nav-item',
// 'nav ul li',
// '[data-category]'
// ]));
/*
* SOLUTION 20:
*
* function benchmarkSelectors(selectors) {
* const results = selectors.map(selector => {
* const start = performance.now();
* for (let i = 0; i < 1000; i++) {
* document.querySelectorAll(selector);
* }
* const end = performance.now();
* return {
* selector,
* time: (end - start).toFixed(3) + 'ms',
* count: document.querySelectorAll(selector).length
* };
* });
*
* return results.sort((a, b) =>
* parseFloat(a.time) - parseFloat(b.time)
* );
* }
*/
// ============================================================
// RUN TESTS
// ============================================================
console.log('DOM Basics Exercises loaded.');
console.log(
'Create the HTML structure and uncomment tests to verify solutions.'
);
console.log('Run in browser environment for full functionality.');