JavaScript JavaScript and the DOM 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 9,047 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
202,084 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.