JavaScript JavaScript and the DOM Traversing the DOM Getting the First and Last Child

Kevin Barnes
Kevin Barnes
16,926 Points

Here's my solution - curious if there's anything I can improve

This frustrated the hell out of me at first, I was trying to re-add & then remove buttons again and had way too much code. Reading through some of the answers here turned me onto the idea of just hiding the unused buttons, this is the end result:

I created the function hideUnusedButtons(), and then called it when the buttons were being added, and during the list modification events. I found that it originally broke when the list only had a single item (it would show Up or Down depending what was on the item before the other item was removed). I've fixed this with a conditional check on the length of the list.

Really keen to hear any feedback for improvements, I'm happy with how little code it took, but I'm always looking to optimise as I learn this stuff.

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;
let firstListItem = listUl.firstElementChild;
let 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 hideUnusedButtons() {
  let first = lis[0].querySelector('button.up');
  let last = lis[lis.length - 1].querySelector('button.down');

  // Hide the necessary buttons for first and last items
  first.style.visibility = 'hidden';
  last.style.visibility = 'hidden';

  // Unhide the previous first and last elements, this only runs if there is >1 item in the list to ensure a single item doesn't have 'Up' or 'Down' buttons
  if (lis.length > 1) {
    let prevFirst = lis[1].querySelector('button.up');
    let prevLast = lis[lis.length - 2].querySelector('button.down');
    prevFirst.style.visibility = 'visible';
    prevLast.style.visibility = 'visible';
  }
}

for (let i = 0; i < lis.length; i++) {
  attachListItemButtons(lis[i]); 
}

hideUnusedButtons();

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);
      }
    }
    hideUnusedButtons();
  }
});

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 = '';
  hideUnusedButtons();
})
Kevin Barnes
Kevin Barnes
16,926 Points

P.S. Here's the workspace with it (hopefully) in action! http://port-80-b0w6osnomu.treehouse-app.com/

1 Answer

KRIS NIKOLAISEN
KRIS NIKOLAISEN
54,288 Points

If you want to share a workspace you need to create a snapshot. Click the camera icon in the upper right corner of the workspace, then 'Take Snapshot' then post the link created here.

For your hideUnusedButtons() function one suggestion would be to check the length of lis before using list items. Below causes an error when lis.length equals 0.

let first = lis[0].querySelector('button.up');
let last = lis[lis.length - 1].querySelector('button.down');