Skip to content

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.

Navigate up to the direct parent:

const heading = document.querySelector('.tour-card h3');
const card = heading.parentElement; // the .tour-card element
const section = card.parentElement; // the section containing the cards

When an event fires on a nested element and you need the containing card, parentElement gets you there.

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 elements
console.log(card.children[0]); // first child element

element.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.

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 card
const section = button.closest('section'); // the containing section

closest 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.

// Given a button click, find the tour card it belongs to
function 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.

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.parentElement navigates up to the direct parent.
  • element.children returns direct child elements (not text nodes). firstElementChild and lastElementChild access the ends directly.
  • nextElementSibling and previousElementSibling move 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() with querySelector inside the result is the pattern behind tour card interactions in Module 07.