1

I'm putting together a simple todo application in which I would like the state of my checkboxes (checked/not checked) to be displayed after refreshing the page based on the information that was added to the localStorage object. Even though the state of the checkboxes are added correctly to the localStorage object, after the page gets refreshed, always the checked state gets applied to the checkboxes, regardless what the previously saved state was. What am I missing?

    const todoInput = document.getElementById('todoInput');
    const addButton = document.getElementById('addButton');
    const todoList = document.getElementById('todoList');
    
    function createListElementByEnter(event) {
        if (event.code === 'Enter') {
            addButton.click();
        }
    }
    
    function createListElementByButton() {
        const checkBox = document.createElement('input');
        checkBox.setAttribute('type', 'checkbox');
        checkBox.setAttribute('id', todoInput.value);
    
        const itemLabel = document.createElement('label');
        itemLabel.setAttribute('for', todoInput.value);
    
        const iconPlaceholder = document.createElement('i');
        iconPlaceholder.classList.add('iconPlaceholder');
        iconPlaceholder.innerHTML = '<i class="fas fa-trash-alt"></i>';
    
        const bottomDivision = document.getElementById('middle');
        const listElement = document.createElement('li');
        const todoInputValue = todoInput.value;
    
        if (todoInputValue) {
            saveItemsToLocalStorageList(todoInputValue, checkBox);
    
            itemLabel.append(todoInputValue);
            listElement.append(checkBox, itemLabel, iconPlaceholder);
            todoList.append(listElement);
            document.body.appendChild(bottomDivision);
        }
    
        todoInput.value = '';
    }
    
    function getItemsFromLocalStorage() {
        const localStorageElements = JSON.parse(localStorage.getItem('listElements'));
    
        if (localStorageElements !== null) {
            localStorageElements.forEach(element => {
                const checkBox = document.createElement('input');
                checkBox.setAttribute('type', 'checkbox');
                checkBox.setAttribute('id', element.itemValue);
                checkBox.setAttribute('checked', element.checkboxState);
    
                const itemLabel = document.createElement('label');
                itemLabel.setAttribute('for', element.itemValue);
    
                const iconPlaceholder = document.createElement('i');
                iconPlaceholder.setAttribute('id', 'iconPlaceholder');
                iconPlaceholder.classList.add('iconPlaceholder');
                iconPlaceholder.innerHTML = '<i class="fas fa-trash-alt"></i>';
    
                const bottomDivision = document.getElementById('middle');
                const listElement = document.createElement('li');
    
                itemLabel.append(element.itemValue);
                listElement.append(checkBox, itemLabel, iconPlaceholder);
                todoList.append(listElement);
                document.body.appendChild(bottomDivision);
            });
        }
    }
    
    function saveItemsToLocalStorageList(todoInputValue, checkbox) {
        let listElements;
    
        if (localStorage.getItem('listElements') === null) {
            listElements = [];
        } else {
            listElements = JSON.parse(localStorage.getItem('listElements'));
        }
    
        listElements.push({itemValue: todoInputValue, checkboxState: checkbox.checked});
    
        localStorage.setItem('listElements', JSON.stringify(listElements));
    }
    
    
    function deleteElementFromList(event) {
        const targetedElement = event.target;
        const itemLabel = targetedElement.parentElement.parentElement.textContent;
    
        if (targetedElement.className === 'fas fa-trash-alt') {
            const listElements = JSON.parse(localStorage.getItem('listElements'));
    
            listElements.forEach(element => {
                if (element.itemValue === itemLabel) {
                    const itemIndex = listElements.indexOf(element);
    
                    listElements.splice(itemIndex, 1);
    
                    localStorage.setItem('listElements', JSON.stringify(listElements));
                }
            });
    
            targetedElement.parentElement.parentElement.remove();
        }
    }
    
    function changeCheckboxState(event) {
        if (event.target.type === 'checkbox') {
            const listElements = JSON.parse(localStorage.getItem('listElements'));
    
            listElements.forEach(element => {
                if (element.itemValue === event.target.id) {
                    element.checkboxState = element.checkboxState === false ? true : false;
                }
            });
    
            localStorage.setItem('listElements', JSON.stringify(listElements));
        }
    }
    
    addButton.addEventListener('click', createListElementByButton);
    todoInput.addEventListener('keyup', createListElementByEnter);
    todoList.addEventListener('click', deleteElementFromList);
    todoList.addEventListener('click', changeCheckboxState);
    document.addEventListener('DOMContentLoaded', getItemsFromLocalStorage);
* {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    
    label {
        word-break: break-all;
    }
    
    input[type=checkbox]:checked + label {
        text-decoration: line-through;
        width: 11.3rem;
    }
    
    input[type=checkbox]:not(checked) + label {
        text-decoration: none;
        width: 11.3rem;
    }
    
    .addButton {
        margin-left: 0.2em;
    }
    
    .topContainer {
        justify-content: center;
        align-items: center;
        min-height: 5vh;
        display: flex;
    }
    
    .middleContainer {
        justify-content: center;
        align-items: center;
        display: flex;
    }
    
    .middleTodoList {
        min-width: 24.5rem;
        list-style: none;
    }
    
    li {
        border: 1px solid black;
        border-radius: 0.5rem;
        max-width: 24.5rem;
        margin-top: 0.5rem;
        padding: 0.3rem;
        color: black;
    }
    
    li label {
        padding-left: 0.5em;
    }
    
    .iconPlaceholder {
       float: right;
    }
<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>My JavaScript Todo App</title>
        <link rel="stylesheet" type="text/css" href="css/myStyle.css">
        <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.14.0/css/all.css">
    </head>
    <body>
    
    <div id="top" class="topContainer">
        <label for="todoInput">
            <input id="todoInput" type="text" size="50">
        </label>
        <button id="addButton" class="addButton">Add</button>
    </div>
    
    <div id="middle" class="middleContainer">
        <ul id="todoList" class="middleTodoList"></ul>
    </div>
    
    </body>
    <script src="js/todo.js"></script>
    </html>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • 1
    This isn't the solution to your question, but FYI, nothing can go after the closing `body` tag, except the closing `html` tag. Your `script` should be just before (not after) the closing `body` tag. – Scott Marcus Aug 16 '20 at 21:50
  • Have you verified that items was stored in local storage in browser settings, Application tab if it's chrome? Does your script even run? Try putting console log in them or debug the code in settings – KeitelDOG Aug 16 '20 at 21:56
  • Thanks for the info @ScottMarcus. – Globetrotter Aug 16 '20 at 22:01
  • 1
    @ScottMarcus answer is correct, checked will render checked sign no matter the value. See this answer https://stackoverflow.com/a/4613192/5565544 – KeitelDOG Aug 16 '20 at 22:09
  • I have verified the items in the localStorage @KeitelDOG using Chrome Debugger. The code was also full of console.logs, I just omitted them here for brevity. I could even see the values that belong to each of the checkboxes change in the localStorage as I was toggling them in the browser window. Even after refreshing as I'm getting the checkbox states out of the localStorage seems everything fine according to the debugger, I'm getting the right states, but I always get a checked checkbox, no matter what state I left the localStorage in before refreshing :/ – Globetrotter Aug 16 '20 at 22:16
  • 1
    @Globetrotter See my answer below for an explanation of why this is happening and the solution, along with a link to a working example. – Scott Marcus Aug 16 '20 at 22:17
  • 1
    Appreciate it @ScottMarcus :) – Globetrotter Aug 16 '20 at 22:24

1 Answers1

3

checked is a Boolean attribute. The mere presence of it in your element means that it should be checked. It makes no difference what value you set it to as you can see when you check the value in localStorage and find that although it is correctly storing false, the checkbox always comes back checked.

See this for more details on above.

So, in your getItemsFromLocalStorage() function, you need to first check to see if the attribute was last set to true and only set the checked attribute if that is the case. If not, don't set the attribute at all.

// First, check to see if the last checked value was true
if(element.checkboxState){
  // And only set the checked attribute if that is the case.
  checkBox.setAttribute('checked', element.checkboxState);
}

While the code below is correct, it won't run here at Stack Overflow because of sandboxing. But, you can test it here.

const todoInput = document.getElementById('todoInput');
    const addButton = document.getElementById('addButton');
    const todoList = document.getElementById('todoList');
    
    function createListElementByEnter(event) {
        if (event.code === 'Enter') {
            addButton.click();
        }
    }
    
    function createListElementByButton() {
        const checkBox = document.createElement('input');
        checkBox.setAttribute('type', 'checkbox');
        checkBox.setAttribute('id', todoInput.value);
    
        const itemLabel = document.createElement('label');
        itemLabel.setAttribute('for', todoInput.value);
    
        const iconPlaceholder = document.createElement('i');
        iconPlaceholder.classList.add('iconPlaceholder');
        iconPlaceholder.innerHTML = '<i class="fas fa-trash-alt"></i>';
    
        const bottomDivision = document.getElementById('middle');
        const listElement = document.createElement('li');
        const todoInputValue = todoInput.value;
    
        if (todoInputValue) {
            saveItemsToLocalStorageList(todoInputValue, checkBox);
    
            itemLabel.append(todoInputValue);
            listElement.append(checkBox, itemLabel, iconPlaceholder);
            todoList.append(listElement);
            document.body.appendChild(bottomDivision);
        }
    
        todoInput.value = '';
    }
    
    function getItemsFromLocalStorage() {
        const localStorageElements = JSON.parse(localStorage.getItem('listElements'));
    
        if (localStorageElements !== null) {
            localStorageElements.forEach(element => {
                const checkBox = document.createElement('input');
                checkBox.setAttribute('type', 'checkbox');
                checkBox.setAttribute('id', element.itemValue);

                // First, check to see if the last checked value was true
                if(element.checkboxState){
                   // And only set the checked attribute if that is the case.
                   checkBox.setAttribute('checked', element.checkboxState);
                }
    
                const itemLabel = document.createElement('label');
                itemLabel.setAttribute('for', element.itemValue);
    
                const iconPlaceholder = document.createElement('i');
                iconPlaceholder.setAttribute('id', 'iconPlaceholder');
                iconPlaceholder.classList.add('iconPlaceholder');
                iconPlaceholder.innerHTML = '<i class="fas fa-trash-alt"></i>';
    
                const bottomDivision = document.getElementById('middle');
                const listElement = document.createElement('li');
    
                itemLabel.append(element.itemValue);
                listElement.append(checkBox, itemLabel, iconPlaceholder);
                todoList.append(listElement);
                document.body.appendChild(bottomDivision);
            });
        }
    }
    
    function saveItemsToLocalStorageList(todoInputValue, checkbox) {
        let listElements;
    
        if (localStorage.getItem('listElements') === null) {
            listElements = [];
        } else {
            listElements = JSON.parse(localStorage.getItem('listElements'));
        }
    
        listElements.push({itemValue: todoInputValue, checkboxState: checkbox.checked});
    
        localStorage.setItem('listElements', JSON.stringify(listElements));
    }
    
    
    function deleteElementFromList(event) {
        const targetedElement = event.target;
        const itemLabel = targetedElement.parentElement.parentElement.textContent;
    
        if (targetedElement.className === 'fas fa-trash-alt') {
            const listElements = JSON.parse(localStorage.getItem('listElements'));
    
            listElements.forEach(element => {
                if (element.itemValue === itemLabel) {
                    const itemIndex = listElements.indexOf(element);
    
                    listElements.splice(itemIndex, 1);
    
                    localStorage.setItem('listElements', JSON.stringify(listElements));
                }
            });
    
            targetedElement.parentElement.parentElement.remove();
        }
    }
    
    function changeCheckboxState(event) {
        if (event.target.type === 'checkbox') {
            const listElements = JSON.parse(localStorage.getItem('listElements'));
    
            listElements.forEach(element => {
                if (element.itemValue === event.target.id) {
                    element.checkboxState = element.checkboxState === false ? true : false;
                }
            });
    
            localStorage.setItem('listElements', JSON.stringify(listElements));
        }
    }
    
    addButton.addEventListener('click', createListElementByButton);
    todoInput.addEventListener('keyup', createListElementByEnter);
    todoList.addEventListener('click', deleteElementFromList);
    todoList.addEventListener('click', changeCheckboxState);
    document.addEventListener('DOMContentLoaded', getItemsFromLocalStorage);
* {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    
    label {
        word-break: break-all;
    }
    
    input[type=checkbox]:checked + label {
        text-decoration: line-through;
        width: 11.3rem;
    }
    
    input[type=checkbox]:not(checked) + label {
        text-decoration: none;
        width: 11.3rem;
    }
    
    .addButton {
        margin-left: 0.2em;
    }
    
    .topContainer {
        justify-content: center;
        align-items: center;
        min-height: 5vh;
        display: flex;
    }
    
    .middleContainer {
        justify-content: center;
        align-items: center;
        display: flex;
    }
    
    .middleTodoList {
        min-width: 24.5rem;
        list-style: none;
    }
    
    li {
        border: 1px solid black;
        border-radius: 0.5rem;
        max-width: 24.5rem;
        margin-top: 0.5rem;
        padding: 0.3rem;
        color: black;
    }
    
    li label {
        padding-left: 0.5em;
    }
    
    .iconPlaceholder {
       float: right;
    }
<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>My JavaScript Todo App</title>
        <link rel="stylesheet" type="text/css" href="css/myStyle.css">
        <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.14.0/css/all.css">
    </head>
    <body>
    
    <div id="top" class="topContainer">
        <label for="todoInput">
            <input id="todoInput" type="text" size="50">
        </label>
        <button id="addButton" class="addButton">Add</button>
    </div>
    
    <div id="middle" class="middleContainer">
        <ul id="todoList" class="middleTodoList"></ul>
    </div>
    
    </body>
    <script src="js/todo.js"></script>
    </html>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71