Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript JavaScript and the DOM (Retiring) Traversing the DOM Sibling Traversal

Octavia Kelly
seal-mask
.a{fill-rule:evenodd;}techdegree
Octavia Kelly
Front End Web Development Techdegree Student 3,556 Points

A delegated click event listener has been attached to the selected ul element, which is stored in the variable list. The

I think my JavaScript code is equivalent to what I've seen in the previous answers relating to this challenge, but I get an error message to the effect that the JavaScript can't evaluate 'p.className'. Why is that, please? Thanks very much!

app.js
const list = document.getElementsByTagName('ul')[0];

list.addEventListener('click', function(e) {
  if (e.target.tagName == 'BUTTON') {
    let button = e.target.tagName;
   let p = button.previousElementSibling;
    p.className += 'highlight';



  }
});
index.html
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript and the DOM</title>
    </head>
    <link rel="stylesheet" href="style.css" />
    <body>
        <section>
            <h1>Making a Webpage Interactive</h1>
            <p>Things to Learn</p>
            <ul>
                <li><p>Element Selection</p><button>Highlight</button></li>
                <li><p>Events</p><button>Highlight</button></li>
                <li><p>Event Listening</p><button>Highlight</button></li>
                <li><p>DOM Traversal</p><button>Highlight</button></li>
            </ul>
        </section>
        <script src="app.js"></script>
    </body>
</html>

3 Answers

Octavia Kelly
seal-mask
.a{fill-rule:evenodd;}techdegree
Octavia Kelly
Front End Web Development Techdegree Student 3,556 Points

Hi Jazz You definitely have helped me! I've thought long and hard about sibling/parent relationships now!! I eventually sorted it: (Not sure how to reproduce original code from screen so here it is anyway!):

let button = event.target; let li = event.target.parentNode; let p = button.previousElementSibling; p.className += "highlight";

Octavia Kelly
seal-mask
.a{fill-rule:evenodd;}techdegree
Octavia Kelly
Front End Web Development Techdegree Student 3,556 Points

Thank you so much for that. I'm still a little confused - why do you need to involve <ul>? Both <button> and <p> are within <li> tags so why aren't <button> and <p> sibling elements, and 'children' of the parent node, <li>? If they were, then the 'previousElementSibling' to <button> would be <p>, because <p> becomes before <button> within the <li> tags. It's the need to include <ul> that's confusing me, I think! Thanks again.

Jazz Jones
Jazz Jones
8,535 Points

if you log the return values to the console in the way that you're describing (selecting li as the parent node of the button and stop the bubble there). then the code recognizes that the other li elements are it's sibling nodes and not the p as the sibling to button.

const list = document.getElementsByTagName('ul')[0];

list.addEventListener('click', function(e) {
  if (e.target.tagName == 'BUTTON') {
    let button = event.target.parentNode;
    let li = button.parentNode;
    let p = li.previousElementSibling;
   console.log(p);
  }
});

The console would then only log the previous li element as it's sibling. I'll be straight up I'm still learning and wrapping my head around this concept, which is why I log everything in the console to see what's going on. I just noticed that it seems the code wasn't traversing all the way back to the button element with the code I just posted.

You could also do

let p = event.target.previousElementSibling;
p.className = 'highlight';
Jazz Jones
Jazz Jones
8,535 Points

So this challenge was tricky for me because I realized visualizing the path through the DOM can be a bit confusing and overwhelming. What I suggest is copy the challenges codes and past them into a testing workspace. During each step trying to solve this, I would log your code to the console. This way it is easier to see what your code is doing and if it's working as expected.

Here is what I did:

const list = document.getElementsByTagName('ul')[0];

list.addEventListener('click', function(e) {
  if (e.target.tagName == 'BUTTON') {
    let li = event.target.parentNode;
   console.log(li);
  }
});

if you look at the console you'll notice that when you click a button, you'll notice that the console logs the li (list item) of the button you clicked. That's because the list item is the parent node of the button element. So let's try and get the parent node of the li element which should be the ul element that the li is nested inside and log it to the console.

const list = document.getElementsByTagName('ul')[0];

list.addEventListener('click', function(e) {
  if (e.target.tagName == 'BUTTON') {
    let li = event.target.parentNode;
    let ul = li.parentNode;
   console.log(ul);
  }
});

Now when you click on a button, you get the ul logged to the console, which shows all of it's children. Great! Now you have access to all nested elements inside the ul element. So how do we get the sibling of the button element, the elusive p element? quite simple actually. you use the previous element sibling method.

const list = document.getElementsByTagName('ul')[0];

list.addEventListener('click', function(e) {
  if (e.target.tagName == 'BUTTON') {
    let li = event.target.parentNode;
    let ul = li.parentNode;
    let p = ul.previousElementSibling;
   console.log(p);
  }
});

now if you log a button click to the console, you'll see only one element is logged. The p element or the sibling of the targeted event. From there it's simple to add a class to the element itself.

const list = document.getElementsByTagName('ul')[0];

list.addEventListener('click', function(e) {
  if (e.target.tagName == 'BUTTON') {
    let li = event.target.parentNode;
    let ul = li.parentNode;
    let p = ul.previousElementSibling;
   p.className = 'highlight';
  }
});

The reason it seems so confusing is because you have to select the parent node in order to gain access to all of it's children. When you click the button, the code is allowing you to bubble up from the node you selected all the way to parent node so the code can find not only the parent's children (i.e. the list items, p elements, and buttons), but also so it can find it's relationship to the element you clicked on. How would the code know that p is a sibling to button, if there isn't anything to reference it's relationship? It would be like missing parts of your family tree and guessing who your brother and sister are.