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

oliverchou
oliverchou
20,886 Points

Sharing my solution to the challenge

This is my solution to the challenge, using 2 functions, one of which includes a 2-level for-loop.

const toggleList = document.getElementById('toggleList');
const listDiv = document.querySelector('.list');
const listUl = listDiv.querySelector('ul');

const descriptionP = document.querySelector('p.description');
const descriptionInput =  document.querySelector('input.description')
const descriptionButton = document.querySelector('button.description');

const addItemInput = document.querySelector('input.addItemInput');
const addItemButton = document.querySelector('button.addItemButton');

const lis = listUl.children;

// starts here
function hideEndBtn () {
  listUl.firstElementChild.firstElementChild.style.visibility = 'hidden';
  listUl.lastElementChild.children[1].style.visibility = 'hidden';
}

function showAllBtn () {
  for(let i=0; i<lis.length; i++) {
    for(let j=0; j<lis[0].childElementCount; j++){
      lis[i].children[j].style.visibility = '';
    }
  }
}

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

for (let i=0; i<lis.length; i++) {
      attachListItemButtons(lis[i]);
     }
hideEndBtn(); // initially hide the unnecessary button

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);      
      }
    }
    showAllBtn();
    hideEndBtn(); // check whenever move a <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.textContent = 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 = '';
  showAllBtn();
  hideEndBtn(); // check whenever add an item
});

2 Answers

I really like the simplicity of your functions! And how you can use them together when the list changes to reset previously hidden buttons as needed. I wrote functions for my answer as well but they are much longer, with a lot of if else statements to get it right in every situation.

I will say that the console throws an error when you delete all of the list items that looks like:

test.js:16 Uncaught TypeError: Cannot read property 'firstElementChild' of null
    at hideEndBtn (test.js:16)
    at HTMLUListElement.<anonymous> (test.js:74)

The error doesn't seem to affect the functionality of the program at all, but you can avoid it by adding an if statement to the first function like so:

function hideEndBtn () {
  if(listUl.firstElementChild) {
    listUl.firstElementChild.firstElementChild.style.visibility = 'hidden';
    listUl.lastElementChild.children[1].style.visibility = 'hidden';
  }
}

Thank you so much for sharing!