Home > Software design >  Toggle classList of a label on checkbox checked
Toggle classList of a label on checkbox checked

Time:02-15

I have a to-do list which I want to be able to cross out tasks as they're completed, as well as delete them by clicking their corresponding "X" button.

I use global variable row which is added to different elements' ID. This is how I differentiate them from other task entries.

This works fine when deleting a task, but not when the checkbox is checked/unchecked. I want to toggle the add-strikethrough CSS class whenever a task's checkbox is clicked.

Relevant HTML:

    <body>
        <h1>To-Do-List</h1>
        <form action="" onsubmit="createElements();return false" id="todo" autocomplete=off>
            <input type="text" maxlength="25" id="enter-task" placeholder="Enter a Task">
            <button type="button" id="submit-task" onClick="createElements()"><span> </span></button>
        </form>

        <h4  id="task-header">current tasks</h4>
            
        <form  id="container">
            <!--This is where the addTask elements go-->
        </form>
        
        
        <script src="script.js"></script>
    </body>

Relevant CSS:

.add-strikethrough{
    text-decoration: line-through;
    text-decoration-color: black;
}

Javascript:

var row = 0;

function createElements(){ 

    //create Checkbox
    var checkbox = document.createElement("input");
    checkbox.setAttribute("type", "checkbox");
    checkbox.setAttribute("id", "entryCheckbox");
    checkbox.setAttribute("value", "unchecked");
    checkbox.setAttribute("onClick", "strikeTask("   row  ");")

    //create label using value from input box
    var label = document.createElement("label");
    label.setAttribute("id", "entryLabel" row);
    label = document.getElementById("enter-task").value;

    //create deleteTask img
    var deleteTask = document.createElement("img");
    deleteTask.src = "images/x.svg";
    deleteTask.setAttribute("id", "delete-task");
    deleteTask.setAttribute("onClick", "deleteRow("   row  ");")
    
    addTask(checkbox, label, deleteTask);
}

//This function appends the elements
function addTask(box, label, x){

    
    const tasks = document.getElementById("container");
    
    const newTask = document.createElement('div');
    newTask.classList.add('task-entries');

    const header = document.getElementById('task-header');

    //Makes sure input field is not empty
    if(label !== ""){

        //Show task container and header
        if(tasks.classList.contains("hide")){
            tasks.classList.replace('hide', 'task-container');
            header.classList.replace('hide', 'show-header')
        };
        //Append newTask to tasks container
        tasks.appendChild(newTask);
            newTask.setAttribute("id", "contentDiv" row);
        newTask.appendChild(box);
        newTask.appendChild(document.createTextNode(label));
        newTask.appendChild(x);
            row  ;
        
        //Resets input field after task is added
        document.getElementById("enter-task").value = null; 
    }
}



function deleteRow(ID){

    document.getElementById('contentDiv' ID).remove();

    tasks = document.getElementById('container');
    header = document.getElementById('task-header');

    //Hide header and task container if no tasks exist
    if(tasks.children.length == 0){
        tasks.classList.replace('task-container', 'hide');
        header.classList.replace('show-header', 'hide');
    }
}

function strikeTask(ID){

    const x = document.getElementById('entryLabel' ID);

    x.classList.toggle('add-strikethrough');
    
}

The strikeTask function is where my problem is; I can't figure out how to apply the class to the label when I click the corresponding checkbox

I appreciate any help!

CodePudding user response:

One way to check what is wrong is to inspect the element created. If you do that you will notice that you don't have a label within a div, only text within a div, because you create a text node with the value of the label, as seen here:

example task element from inspect

You cannot apply a class to a text node. So you have to create the label element, add text to the label element, then append it.

Here is updated Javascript that will do that:

var row = 0;

function createElements(){ 

    //create Checkbox
    var checkbox = document.createElement("input");
    checkbox.setAttribute("type", "checkbox");
    checkbox.setAttribute("id", "entryCheckbox");
    checkbox.setAttribute("value", "unchecked");
    checkbox.setAttribute("onClick", "strikeTask("   row  ");")

    //create label using value from input box
    var label = document.createElement("label");
    label.setAttribute("id", "entryLabel" row);
    label.textContent = document.getElementById("enter-task").value;

    //create deleteTask img
    var deleteTask = document.createElement("img");
    deleteTask.src = "images/x.svg";
    deleteTask.setAttribute("id", "delete-task");
    deleteTask.setAttribute("onClick", "deleteRow("   row  ");")
    
    addTask(checkbox, label, deleteTask);
}

//This function appends the elements
function addTask(box, label, x){

    
    const tasks = document.getElementById("container");
    
    const newTask = document.createElement('div');
    newTask.classList.add('task-entries');

    const header = document.getElementById('task-header');

    //Makes sure input field is not empty
    if(label !== ""){

        //Show task container and header
        if(tasks.classList.contains("hide")){
            tasks.classList.replace('hide', 'task-container');
            header.classList.replace('hide', 'show-header')
        };
        //Append newTask to tasks container
        tasks.appendChild(newTask);
            newTask.setAttribute("id", "contentDiv" row);
        newTask.appendChild(box);
        newTask.appendChild(label);
        newTask.appendChild(x);
            row  ;
        
        //Resets input field after task is added
        document.getElementById("enter-task").value = null; 
    }
}



function deleteRow(ID){

    document.getElementById('contentDiv' ID).remove();

    tasks = document.getElementById('container');
    header = document.getElementById('task-header');

    //Hide header and task container if no tasks exist
    if(tasks.children.length == 0){
        tasks.classList.replace('task-container', 'hide');
        header.classList.replace('show-header', 'hide');
    }
}

function strikeTask(ID){

    const x = document.getElementById('entryLabel' ID);

    x.classList.toggle('add-strikethrough');
    
}

What I've changed: label = document.getElementById("enter-task").value; to label.textContent = document.getElementById("enter-task").value; inside createElements() & newTask.appendChild(document.createTextNode(label)); to newTask.appendChild(label); inside addTask(...).

The label is created as you were creating it; its text content is set to the value of the enter-task input; the element is appended.

What you were doing was create a label element and giving it an id, but on the following line (label = document.getElementById("enter-task").value;) changing the value of the label variable to a text, then adding that text as a text node (newTask.appendChild(document.createTextNode(label));).

  • Related