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

'Getting the parentNode of a parentNode'.

What exactly does Guil Hernandez mean when he says, Notice how I'm getting the parentNode of a parentNode?

listUL.addEventListener('click', (event) => {
  if (event.target.tagName == 'BUTTON') {
    let li = event.target.parentNode;
    let ul = li.parentNode;
    ul.removeChild(li);
  }
});

He was referring to this 2 lines of codes

let li = event.target.parentNode;
    let ul = li.parentNode;

Here's the link to the video: https://teamtreehouse.com/library/using-parentnode-to-traverse-up-the-dom

Time: 04:17

Steven Parker

3 Answers

Steven Parker
Steven Parker
243,318 Points

:bell: Hi, I was notified by your tag.

This code does use a delegated handler (with "event bubbling"), but that's not related to the DOM traversal being done here. The traversal code would work the same in a direct handler.

So here's those two lines again with comments added:

let li = event.target.parentNode;  // <-- this gets the parent of the button (event target)
    let ul = li.parentNode;        // <-- this gets the parent of the "li"

So "ul" is then the parent of the parent of the button (the event target).

So the parent of the button is the li and ul is the parent of li and the button? Is that what you mean? So he's targeting the parent (ul) which is the parent of li, which is the parent of button?

But then I have another question. Why was it necessary to do that? And in an unrelated matter, I'm trying to use this DOM traversing to solve a problem and it's giving me a headache.

so here's my code for my website I'm working on.

<section class="clearfix about-wrap"><!--Break up a page into logical groupings of info-->
        <div class="me col">

          <img src="img/sam.jpg" alt="Photograph of Sam" class="profile-photo">
          <h3>Samuel</h3>
          <p>Hi I'm Samuel. This is a family site where I share photos and recent information about my immediate family with my extended family.</p>
          <p>If you'd like to follow me on twitter, my username is <a href="http://twitter.com/sunshanmu">@sunshanmu.</a></p>

          <div id="gradientSam">
            <button type="button" name="button" class="buttonSam">Show more about Sam</button>
          </div>

        </div>

        <div class="muci col">
          <img src="img/muci.jpg" alt="Photograph of Muci" class="profile-photo">
          <h3>Muci</h3>
          <p>Hi I'm Muci. I'm the beautiful wife of the creator of this website. I'm talented, smart, I try to be funny.</p>
          <p>If you'd like to follow me on twitter, my username is <a href="http://twitter.com/sunshanmu">@muci.</a></p>

          <div id="gradientMuci">
          <button type="button" name="button" class="buttonMuci">Get to Know Muci</button>
          </div>

        </div>

        <div class="nate col">
          <img src="img/nate.jpg" alt="Photograph of Nate" class="profile-photo">
          <h3>Nathaniel</h3>
          <p>Hi I'm Nate. I'm the love of my two amazing parents. I love them very much and I'm very happy to be a part of this family.</p>
          <p class="nate-last-p">If you'd like to follow me on twitter, my username is <a href="http://twitter.com/sunshanmu">@nate.</a></p>

          <div id="gradientNate">
          <button type="button" name="button" class="buttonNate">Get to Know Nate</button>
        </div>
        </div>
      </section>
button {
    font-size: .85em;
    padding: .85em 2em;
    color: #fff;
    background: #3acec2;
    outline: 0;
    border: solid 1px;
    border-color: rgba(0, 0, 0, .1);
  cursor: pointer;
    display: block;
    margin: 2em auto;
}

.col {
    position: relative;
    margin-bottom: 60px;
    text-align: center;
}

#gradientSam,
#gradientMuci,
#gradientNate {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 70%;
    background: linear-gradient(0deg, #fff, #fff, transparent 50%);
    /*linear-gradient(0deg, #fff, transparent 100%);*/
}

        .buttonSam,
    .buttonMuci,
    .buttonNate {
        position: absolute;
        bottom: 0;
        left: 1em;
        margin-top: auto;
        margin-right: auto;
    }

    .nate .nate-last-p {
        margin-bottom: 150px;
    }
const revealSam = document.getElementById('gradientSam');
const revealMuci = document.getElementById('gradientMuci');
const revealNate = document.getElementById('gradientNate');
const samText = document.querySelector('.buttonSam');
const muciText = document.querySelector('.buttonMuci');
const nateText = document.querySelector('.buttonNate');

revealSam.addEventListener('click', () => {
  if (revealSam.style.height == '100%') {
    samText.textContent = 'Show more about Sam';
    revealSam.style.height = '70%';
    revealSam.style.transition = 'height .5s';
    revealSam.style.background = 'linear-gradient(0deg, #fff, #fff, transparent 50%)';
  } else {
    samText.textContent = 'Show less about Sam';
    revealSam.style.height = '100%';
    revealSam.style.transition = 'height .5s';
    revealSam.style.background = 'linear-gradient(0deg, #fff, #fff, transparent 30%)';
  }
});

revealMuci.addEventListener('click', () => {
  if (revealMuci.style.height == '100%') {
    muciText.textContent = 'Show more about Muci';
    revealMuci.style.height = '70%';
    revealMuci.style.transition = 'height .5s';
    revealMuci.style.background = 'linear-gradient(0deg, #fff, #fff, transparent 50%)';
  } else {
    muciText.textContent = 'Show less about Muci';
    revealMuci.style.height = '100%';
    revealMuci.style.transition = 'height .5s';
    revealMuci.style.background = 'linear-gradient(0deg, #fff, #fff, transparent 30%)';
  }
});

revealNate.addEventListener('click', () => {
  if (revealNate.style.height == '100%') {
    nateText.textContent = 'Show more about Nate';
    revealNate.style.height = '70%';
    revealNate.style.transition = 'height .5s';
    revealNate.style.background = 'linear-gradient(0deg, #fff, #fff, transparent 50%)';
  } else {
    nateText.textContent = 'Show less about Nate';
    revealNate.style.height = '100%';
    revealNate.style.transition = 'height .5s';
    revealNate.style.background = 'linear-gradient(0deg, #fff, #fff, transparent 30%)';
  }
});

What I'm trying to do, as you probably figured out, is make the show more/less about Sam button slide down and up respectively revealing more or showing less when clicked. Sort of like the toggleList example we did a few lessons ago. I should be able to do it with the amount of javaScript I've done by now, but can't figure out how to do it using DOM traversal. I got it working with the code above, but as you can see it's a lot of unnecessary code. Also while going over my notes trying to figure this out, I realized the lesson where we added the remove item button that removed a list item when clicked, won't work if you had two listDiv. I know because I copied and pasted the list div in the html, but it only worked on the first listDiv.

video link for toggleList: https://teamtreehouse.com/library/styling-elements

video link for remove item button: https://teamtreehouse.com/library/using-parentnode-to-traverse-up-the-dom

So my questions are,

  1. how would you make this code work if you had two list div with list that you wanted to remove list items? it's similar to my html where every button is in it's own div and while the divs have the same class/ID it doesn't target them when clicked, using the event.target method.
  2. how could I get the effect above with less code and using the DOM traversal method?
  3. You might have noticed that I added the button inside a separate div inside the div of Sam and Muci which has a gradient that hides some of the text . Is that the best way to do this effect, if not how would you suggest I do it?
  4. Can you please list all the problems with my program? Thanks. Steven Parker

Hi Samuel, what Guil means is that he's using event bubbling to target the parent of the remove button (which is the list item that contains the name, confirmed checkbox and the remove button) and then target the parent of that particular list item (that contains the clicked remove button) which is the unordered list by making use of the event object. He could make us of event bubbling and the event object because he attached the event handler to the parent of all list items in the RSVP app which is the unordered list with an id of 'invitedList', this allows the event object handle the same event on its children.

Steven Parker
Steven Parker
243,318 Points

For the benefit of other readers, and to give yourself the best chance at more responses, it's always a good idea to start a new question for a change of topic.

But since it's already here this time ...
You can reduce the code by using a single delegated handler attached to the nearest common parent (the selection in this case), and filtering on the elements you want to react to:

const sect = document.querySelector("section");

sect.addEventListener("click", e => {
  if (e.target.id.startsWith("gradient")) {   // one of the DIVs was clicked
    mydiv = e.target;
    mybtn = mydiv.children[0];
  } else if (e.target.tagName == "BUTTON") {  // an inner button was clicked
    mybtn = e.target;
    mydiv = mybtn.parentNode;
  } else return;
  if (mydiv.style.height == "100%") {
    mybtn.textContent = mybtn.textContent.replace("less", "more");
    mydiv.style.height = "70%";
    mydiv.style.transition = "height .5s";
    mydiv.style.background = "linear-gradient(0deg, #fff, #fff, transparent 50%)";
  } else {
    mybtn.textContent = mybtn.textContent.replace("more", "less")
                        .replace("Get to Know", "Show less about");
    mydiv.style.height = "100%";
    mydiv.style.transition = "height .5s";
    mydiv.style.background = "linear-gradient(0deg, #fff, #fff, transparent 30%)";
  }
});

Also, instead of covering up the text with an overlay (but still having it take up space), I might place the text inside the element that expands and collapses. Then you wouldn't need the absolute positioning, either.

Hi Steven Parker this works great but I see a lot of methods that I know nothing about, so this is advanced for me. I'm sure I will get it later on as I continue this. I understand some of it.

And in future I will post new topics in the forum. What you said makes sense.

Thanks a lot.