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 Getting the First and Last Child

Doron Geyer
seal-mask
.a{fill-rule:evenodd;}techdegree
Doron Geyer
Full Stack JavaScript Techdegree Student 13,897 Points

Answer, inelegant but effective.

const toggleList = document.getElementById("toggleList");
const listDiv = document.querySelector(".list");
const descriptionInput = document.querySelector("input.description");
const descriptionP = document.querySelector("p.description");
const descriptionButton = document.querySelector("button.description");
const listUl = listDiv.querySelector("ul");
const addItemInput = document.querySelector("input.addItemInput");
const addItemButton = document.querySelector("button.addItemButton");
const lis = listUl.children;
const firstListItem = listUl.firstElementChild;
const lastListItem = listUl.lastElementChild;

function attachListItemButtons(li) {
  let up = document.createElement("button");
  up.className = "up";
  up.textContent = "Up";
  li.appendChild(up);

  let down = document.createElement("button");
  down.className = "down";
  down.textContent = "Down";
  li.appendChild(down);

  let remove = document.createElement("button");
  remove.className = "remove";
  remove.textContent = "Remove";
  li.appendChild(remove);
}

// function removes all button elements from the list items.
function resetButtons() {
  for (let i = 0; i < lis.length; i += 1)
    while (lis[i].lastElementChild) {
      lis[i].removeChild(lis[i].lastElementChild);
    }
}

//function checks the list items and removes the first child of the top one if it is an "up" button
// it also removes the last list items second child if its a down button;
function removeUnusedButtons() {
  let liFirst = lis[0];
  let upBtn = liFirst.firstElementChild;
  if (upBtn.className == "up") {
    liFirst.removeChild(upBtn);
  }

  let liLast = lis[lis.length - 1];
  let downBtn = liLast.firstElementChild.nextElementSibling;
  if (downBtn.className == "down") {
    liLast.removeChild(downBtn);
  }
}

//Function Calls on initial Run.
for (let i = 0; i < lis.length; i += 1) {
  attachListItemButtons(lis[i]);
}
removeUnusedButtons();
// Event Listeners

listUl.addEventListener("click", (event) => {
  if (event.target.tagName == "BUTTON") {
    if (event.target.className == "remove") {
      let li = event.target.parentNode;
      let ul = li.parentNode;
      ul.removeChild(li);
    }
    if (event.target.className == "up") {
      let li = event.target.parentNode;
      let prevLi = li.previousElementSibling;
      let ul = li.parentNode;
      if (prevLi) {
        ul.insertBefore(li, prevLi);
      }
    }
    if (event.target.className == "down") {
      let li = event.target.parentNode;
      let nextLi = li.nextElementSibling;
      let ul = li.parentNode;
      if (nextLi) {
        ul.insertBefore(nextLi, li);
      }
    }
  }
});

toggleList.addEventListener("click", () => {
  if (listDiv.style.display == "none") {
    toggleList.textContent = "Hide list";
    listDiv.style.display = "block";
  } else {
    toggleList.textContent = "Show list";
    listDiv.style.display = "none";
  }
});

descriptionButton.addEventListener("click", () => {
  descriptionP.innerHTML = descriptionInput.value + ":";
  descriptionInput.value = "";
});

addItemButton.addEventListener("click", () => {
  let ul = document.getElementsByTagName("ul")[0];
  let li = document.createElement("li");
  li.textContent = addItemInput.value;
  attachListItemButtons(li);
  ul.appendChild(li);
  addItemInput.value = "";
});
// Added the event listener here for simplicity sake, on clicking any button inside the list area it resets buttons 
// attaches buttons to each list item and removes the up and down buttons from the end.
listDiv.addEventListener("click", (e) => {
  if ((e.target.tagName = "BUTTON")) {
    resetButtons();
    for (let i = 0; i < lis.length; i += 1) {
      attachListItemButtons(lis[i]);
    }
    removeUnusedButtons();
  }
});

I struggled to integrate the new functions with the event listeners/handlers from the original exercise. This led me to put the event listener on the list div effectively running the resetbuttons, addbuttons and remove up/down buttons on any button click on the page.

If I had to do this in practice I would just have disabled the up and down buttons rather than try to remove them which would both look better and be functionally much simpler.

How could this be done logically so that the process wasnt remove buttons, add buttons, remove redundant buttons. but perhaps more concise/efficient?

1 Answer

Steven Parker
Steven Parker
231,270 Points

The intention of the exercise is to help you learn techniques, not create the best possible UX.

But I agree that disable/enable is much better for creating an intuitive UX than removing the buttons entirely, as well as more efficient to implement in code.