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 DOM Scripting By Example Improving the Application Code Next Steps

Steve Gallant
Steve Gallant
14,943 Points

interacting with HTML retreived from localStorage (via JSON)

Hello there, I've managed to get most of the enhancements for this RSVP app done on my own, but having trouble with localStorage. I'm able to save the ul using JSON and then pull it back up when the page is re-loaded. However, the loaded LI's are not interactive, i.e. there don't appear to be any eventListeners active on their child elements (checkbox, buttons, etc). Furthermore, adding a new invitee via the main input field, it doesn't appear in the ul with the pre-loaded invitees from localStorage.

I think I'm almost there, any tips would be appreciated. Thanks! Steve

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>RSVP App</title>
  <link href="https://fonts.googleapis.com/css?family=Courgette" rel="stylesheet">
  <link href="https://fonts.googleapis.com/css?family=Lato:400,700" rel="stylesheet">
  <link href="css/style.css" rel="stylesheet">
</head>
<body>
  <div class="wrapper">
    <header>
      <h1>RSVP</h1>
      <p>A Treehouse App</p>
      <form id="registrar">
        <input type="text" name="name" placeholder="Invite Someone">
        <button type="submit" name="submit" value="submit">Submit</button>
      </form>
    </header>

    <div class="main">  
      <h2>Invitees</h2>
      <ul id="invitedList"></ul>    
    </div>
  </div>
  <script type="text/javascript" src="app.js"></script>
</body>
</html>
document.addEventListener('DOMContentLoaded', () => {
  const form = document.getElementById('registrar');
  const input = form.querySelector('input');

  const mainDiv = document.querySelector('.main');
  const ul = document.getElementById('invitedList');

  const div = document.createElement('div');
  const filterLabel = document.createElement('label');
  const filterCheckBox = document.createElement('input');


  filterLabel.textContent = "Hide those who haven't responded";
  filterCheckBox.type = 'checkbox';
  div.appendChild(filterLabel);
  div.appendChild(filterCheckBox);
  mainDiv.insertBefore(div, ul);

  if (localStorage.invitees) {
    ul.outerHTML = JSON.parse(localStorage.invitees);
  }


  filterCheckBox.addEventListener('change', (e) => {
    const isChecked = e.target.checked;
    const lis = ul.children;
    if(isChecked) {
      for (let i = 0; i < lis.length; i += 1) {
        let li = lis[i];
        if (li.className === 'responded') {
          li.style.display = '';
          // Hide the checkbox and label for confirmed invitees when
          // filter is activated
          li.getElementsByTagName('label')[0].style.display = 'none';
          // console.log(li.getElementsByTagName('label')[0]);
        } else {
          li.style.display = 'none';
        }
      }
    } else {
      for (let i = 0; i < lis.length; i += 1) {
        let li = lis[i];
        li.style.display = '';
        // Show checkbox and label again when filter is removed
        li.getElementsByTagName('label')[0].style.display = '';
      }
    }
  });

  function createLI(text) {
    function createElement(elementName, property, value) {
      const element = document.createElement(elementName);
      element[property] = value;
      return element;
    }

    function appendToLI(elementName, property, value) {
      const element = createElement(elementName, property, value);
      li.appendChild(element);
      return element;
    }

    const li = document.createElement('li');
    appendToLI('span', 'textContent', text);
    appendToLI('label', 'textContent', 'Confirm?')
      .appendChild(createElement('input', 'type', 'checkbox'));
    appendToLI('button', 'textContent', 'edit');
    appendToLI('button', 'textContent', 'remove');
    return li;
  }

  form.addEventListener('submit', (e) => {
    e.preventDefault();
    const text = input.value;
    // disallow duplicate names for invitees
    let duplicate = false;
    if (ul.children.length > 0) {
      for (i = 0; i < ul.children.length; i++) {
        if (text.toLowerCase() === ul.children[i].firstElementChild.textContent.toLowerCase()) {
          duplicate = true;
          console.log(duplicate);
        }
      }
    }
    // check for empty string and disallow
    if (text === '') {
      alert('Please enter a valid name');
    } else if (duplicate) {   // check for duplicate names
      alert("Duplicate names not allowed");
      duplicate = false;
    } else {
      input.value = '';
      const li = createLI(text);
      ul.appendChild(li);

      // store ul in localStorage
      console.log(ul.outerHTML);
      localStorage.setItem('invitees', JSON.stringify(ul.outerHTML));
    }

  });

  ul.addEventListener('change', (e) => {
    const checkbox = event.target;
    const checked = checkbox.checked;
    const listItem = checkbox.parentNode.parentNode;
    const label = checkbox.parentNode;

    if (checked) {
      listItem.className = 'responded';
      label.childNodes[0].nodeValue = 'Confirmed';
    } else {
      listItem.className = '';
      label.childNodes[0].nodeValue = 'Confirm?';
    }
  });

  ul.addEventListener('click', (e) => {
    if (e.target.tagName === 'BUTTON') {
      const button = e.target;
      const li = button.parentNode;
      const ul = li.parentNode;
      const action = button.textContent;
      const nameActions = {
        remove: () => {
          ul.removeChild(li);
        },
        edit: () => {
          const span = li.firstElementChild;
          const input = document.createElement('input');
          input.type = 'text';
          input.value = span.textContent;
          li.insertBefore(input, span);
          li.removeChild(span);
          button.textContent = 'save';
        },
        save: () => {
          const input = li.firstElementChild;
          const span = document.createElement('span');
          span.textContent = input.value;
          li.insertBefore(span, input);
          li.removeChild(input);
          button.textContent = 'edit';
        }
      };

      // select and run action in button's name
      nameActions[action]();
    }
  });


});

1 Answer

Jay Reyes
seal-mask
.a{fill-rule:evenodd;}techdegree
Jay Reyes
Python Web Development Techdegree Student 15,937 Points

I'm also experiencing this issue. I'm able to save the invitees and they get loaded.

The problem is that the buttons for the any list item is not interactive.

e: they became interactive for me I just forgot the "()" in nameActionsaction

Steve Gallant
Steve Gallant
14,943 Points

Hi Jay, I'm unclear if you are saying you found the error in my code above? If so, could you please be more specific? I do have the closed parens when calling nameActions...

thanks! Steve