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

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

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

1 Answer

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');