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

Neil McPartlin
Neil McPartlin
14,662 Points

Using localStorage: Best way forward?

The objective would be to save to localStorage, the complete const 'ul' (document.getElementById('invitedList')), then use this to rebuild 'ul' when restarting the app.

The tricky part though is that localStorage can only store strings, so arrays and objects need to be converted. I have tried with JSON.stringify to make the necessary conversion but no joy so far. I suspect some preliminary conversions need to be done first. I'm conscious that the above would also contain buttons and checkboxes which may be regarded as overkill and wasteful of the available storage space.

It could be a different approach is required. Instead I should just look to create and save a JSON.stringified object made up of pairs of data e.g. 'name' : 'status', (e.g. ['mary' : 'confirm' , 'john' : ' confirmed'] then use this data to repopulate a new empty 'ul'.

I did watch the localStorage workshop, but only simple strings were being stored and retrieved. I'm not looking for a final solution right now, just a recommendation on the best approach.

Steven Parker
Steven Parker
229,644 Points

I would have expected the JSON conversions to be ideal for storing objects in local storage. Perhaps you could share some code that you're having trouble with.

Neil McPartlin
Neil McPartlin
14,662 Points

Thanks Steven. Here is a snapshot. This is based on Guil's code. My other experiments are saved elsewhere. https://w.trhou.se/iixu4p2l7m

index.html is just using app.js

line 55: I am console logging what I'd like to store.

line 56: I have entered localStorage.setItem('rsvp', JSON.stringify(ul)); as a test. This should update each time a new user is added. But in Chrome dev tools -> Application -> Storage -> LocalStorage -> [Local URL] all I see as a 'Key:Value' pair is 'rsvp:{}'.

Based on your reply, I'm guessing 'ul' is not yet in a format compatible with JSON.stringify . We've not covered JSON yet so perhaps ul needs to be converted to JSON first?

Steven Parker
Steven Parker
229,644 Points

Instead of passing "ul" directly to JSON.stringify, try passing "ul.outerHTML" instead.

Neil McPartlin
Neil McPartlin
14,662 Points

Yes, thanks Steven, big step in the right direction. I now see meaningful data in localStorage.

But there is one oddity...

If ul contains 2 Invitees, ul.outerHTML only shows the 1st one. If ul contains 4 Invitees, ul.outerHTML only shows the 1st three, and so on. I see this with a simple console log too.

So to illustrate, I will just add 2 Invitees, then show the outputs of both console logs.

line 55 of app.js: 
console.log(ul);

In the console: 
<ul id="invitedList"><li><span>neil</span><label>Confirmed<input type="checkbox"></label><button>Edit</button><button>Remove</button></li><li><span>liam</span><label>Confirmed<input type="checkbox"></label><button>Edit</button><button>Remove</button></li></ul>
line 56 of app.js: 
console.log(ul.outerHTML);

In the console: 
<ul id="invitedList"><li><span>neil</span><label>Confirmed<input type="checkbox"></label><button>Edit</button><button>Remove</button></li></ul>

I welcome your thoughts.

1 Answer

Steven Parker
Steven Parker
229,644 Points

You'll notice that the console.log call that is inside the code will show the same thing that is placed in local storage, but the one you run by hand shows more. That's happening because you're updating the local storage in the "createLI" function that creates a new list item. If you want the new item to be included in the storage, you should move the setItem call out of that function and place it in the submit event handler, after the call to appendChild where the new list item is added to the ul.

Happy coding!

Neil McPartlin
Neil McPartlin
14,662 Points

Hi Steven. First of all you are spot on about this. Thank you.

If you want the new item to be included in the storage, you should move the setItem call out of that function and place it in the submit event handler, after the call to appendChild where the new list item is added to the ul.

Leaving aside localStorage completely, what's currently confusing me is that neither console.logs were done by hand. They were one above the other within the code, both inside the createLI function. So had console.log(uL) matched console.log(ul.outerHTML), with both missing the last added entry, then I would have known I was in the wrong place.

So I'm left unable to explain (to myself) the difference. We know ul is a global const whereas maybe the value associated with ul.outerHTML varies depending on whether it is viewed outside a function or within specific functions.

Anyway. I'm up and running with this localStorage challenge so many thanks.

Steven Parker
Steven Parker
229,644 Points

I am also confused! When I ran the code in the snapshot I did indeed see the contents of the UL displayed as they were before the new item was added. But if I typed a console.log command into the console I would see then entire UL. This was try with just ul or ul.outerHTML.

Neil McPartlin
Neil McPartlin
14,662 Points

I hope you're confused like I am, and not by me ;-)

Manually typing both console logs in the console (ul or ul.outerHTML) does return the full listing of Invitees. But they differ when included within the code for the 'createLI' function. Agreed?

I just revisited the snapshot I posted to make sure it still behaves the way I describe.

Steven Parker
Steven Parker
229,644 Points

I don't see any visible difference between calling "console.log(ul);" and "console.log(ul.outerHTML);" inside the function, which is as I would expect.

Neil McPartlin
Neil McPartlin
14,662 Points

Ahh, so we are not witnessing the same thing. I note you have introduced console.info into the storyline. Here is what I have done. Using the snapshot I provided earlier, I have edited line 55 and added new lines 56 and 57. These are the 3 lines.

  console.log("console.log of ul = ", ul);
  console.log("console.log of ul.outerHTML = ", ul.outerHTML);
  console.info("console.info of ul.outerHTML = ", ul.outerHTML);

I then added one Invitee, cleared the console, added a second Invitee, and this is what I see in my console.

https://drive.google.com/file/d/0B2uOLmMTsvHUdXFOdlo0a0JlQ3c

console.log(ul) shows both Invitees whereas the other two only show one.

Steven Parker
Steven Parker
229,644 Points

I see now. You were using the devtool's ability to examine the element directly, which shows you the current state of the element, not the state it had when the console.log was performed. Displaying the outerHTML give you a frozen snapshot of the element in the devtools, while displaying the element itself causes the devtools to display a live link.

If you breakpoint the code anywhere within that function you'll see the outputs are the same at that time.

And I used "info" just to give the output a different appearance from "log", which you did by adding the strings.

Neil McPartlin
Neil McPartlin
14,662 Points

And there it is! Thanks Steven.

... while displaying the element itself causes the devtools to display a live link.

A live link! This is a lesson I won't forget in a hurry :)

Andrew Lundy
Andrew Lundy
13,269 Points

I'm gonna chime in here because I need some help. I've added the setItem call into the submit event handler, now how do I actually get it to re-show the list when I refresh the page?

form.addEventListener('submit', (e) => {
        e.preventDefault();
        const text = input.value;
        input.value = '';

        // Checks for empty string in the input area
        if (text === '') {
            alert("You have not entered a name, please try again.");
            return;
        }
        // Checks for duplicate names
        for (i = 0; i < ul.children.length; i++) {
            if (text === ul.children[i].children[0].textContent) {
                alert("This name has already been entered. Please enter a different name.");
                return;
            }
        }

        const li = createLI(text);
        ul.appendChild(li);
        localStorage.setItem('rsvp', JSON.stringify(ul.outerHTML));

    });
Steven Parker
Steven Parker
229,644 Points

Next time, create a new post for your new question.

But to reload the previously stored list you can do this:

if (localStorage.rsvp)
    ul.outerHTML = JSON.parse(localStorage.rsvp);