Traversing the DOM
Selectors find elements by CSS rules. Traversal moves from a known element to its relatives in the tree — up to its parent, down to its children, sideways to its siblings. This is essential for event delegation in Module 06 and for building the tour card list in Module 07.
element.parentElement
Section titled “element.parentElement”Navigate up to the direct parent:
const heading = document.querySelector('.tour-card h3');const card = heading.parentElement; // the .tour-card elementconst section = card.parentElement; // the section containing the cardsWhen an event fires on a nested element and you need the containing card, parentElement gets you there.
element.children
Section titled “element.children”Returns an HTMLCollection of all direct child elements (text nodes excluded):
const card = document.querySelector('.tour-card');console.log(card.children.length); // number of direct child elementsconsole.log(card.children[0]); // first child elementelement.childNodes includes text nodes and comment nodes — usually not what you want. Use children for element-only traversal.
element.firstElementChild and element.lastElementChild
Section titled “element.firstElementChild and element.lastElementChild”Direct access to the first and last child elements:
const section = document.querySelector('.tours-section');const firstCard = section.firstElementChild;const lastCard = section.lastElementChild;element.nextElementSibling and element.previousElementSibling
Section titled “element.nextElementSibling and element.previousElementSibling”Move horizontally to adjacent siblings:
const firstCard = document.querySelector('.tour-card');const secondCard = firstCard.nextElementSibling;const alsoFirstCard = secondCard.previousElementSibling;Returns null if there is no sibling in that direction.
element.closest()
Section titled “element.closest()”closest(selector) walks up the ancestor chain from the element and returns the first ancestor matching the selector:
const button = document.querySelector('.tour-card button');const card = button.closest('.tour-card'); // the containing cardconst section = button.closest('section'); // the containing sectionclosest is the key to event delegation — attaching one event listener to a parent and using event.target.closest('.tour-card') to identify which card was clicked. You will use this pattern in Module 06.
Combining traversal and selection
Section titled “Combining traversal and selection”// Given a button click, find the tour card it belongs tofunction handleCardClick(event) { const card = event.target.closest('.tour-card'); if (!card) return; const title = card.querySelector('h3').textContent; console.log('Clicked card:', title);}This pattern — event.target.closest() followed by querySelector within the found element — is how Module 07 implements the tour filter and favorites features.
STO capstone: rendering tour cards to the page
Section titled “STO capstone: rendering tour cards to the page”This is the first time your JavaScript writes to the actual STO page. The renderTourCard function from Module 04 returns an HTML string — now you insert it into the DOM:
const formatPrice = price => '$' + price.toFixed(2);
const tours = [ { name: 'Cascade Ridge Hike', price: 149, available: true, category: 'hiking', img: 'images/cascade-ridge-hike.jpg', alt: 'Pine forest with sunlit mountains on the Cascade Ridge Hike' }, { name: 'Summit Loop Trek', price: 199, available: true, category: 'hiking', img: 'images/summit-loop-trek.jpg', alt: 'Green trees near a mountain lake on the Summit Loop Trek' }, { name: 'Valley Floor Walk', price: 99, available: false, category: 'walking', img: 'images/valley-floor-walk.jpg', alt: 'Fern-lined forest path on the Valley Floor Walk' },];
function renderTourCard(tour) { const status = tour.available ? 'Available' : 'Sold Out'; return ` <article class="tour-card" data-category="${tour.category}"> <div class="tour-card-image-wrap"> <img class="tour-card-image" src="${tour.img}" alt="${tour.alt}"> </div> <div class="tour-card-body"> <h3>${tour.name}</h3> <p class="tour-price">${formatPrice(tour.price)}</p> <p class="tour-status">${status}</p> </div> </article> `;}
const container = document.querySelector('.tours-grid');if (container) { for (const tour of tours) { container.insertAdjacentHTML('beforeend', renderTourCard(tour)); }}Each iteration appends one tour card. After the loop, all three cards appear on the page — generated entirely from JavaScript data.
Exercise
Section titled “Exercise”Run the following in the Console on index.html:
const container = document.querySelector('.tours-grid');console.log('Container found:', container);
const tours = [ { name: 'Cascade Ridge Hike', price: 149, available: true, category: 'hiking' }, { name: 'Summit Loop Trek', price: 199, available: true, category: 'hiking' }, { name: 'Valley Floor Walk', price: 99, available: false, category: 'walking' },];
for (const tour of tours) { const status = tour.available ? 'Available' : 'Sold Out'; container.insertAdjacentHTML('beforeend', ` <p>${tour.name} — $${tour.price.toFixed(2)} — ${status}</p> `);}Verify all three entries appear on the page. Then use container.firstElementChild, .lastElementChild, and .nextElementSibling in the console to navigate the result.
element.parentElementnavigates up to the direct parent.element.childrenreturns direct child elements (not text nodes).firstElementChildandlastElementChildaccess the ends directly.nextElementSiblingandpreviousElementSiblingmove horizontally to adjacent siblings.element.closest(selector)walks up the ancestor chain to the first matching ancestor — essential for event delegation.- Combining
event.target.closest()withquerySelectorinside the result is the pattern behind tour card interactions in Module 07.