简体   繁体   中英

Why does my attempt to add and remove classes to an HTML-element only work one way?

Very basic task, don't worry about the coding style itself. I'm sure there are way more efficent and prettier ways of coding. The question is why the code doesn't work the way it is supposed to: It should add class "a" and remove class "b" and vice versa (depending on the class that is currently attached). It only works one way though, the class "closed" can be attached, but then "open" would not work anymore. Why is that?

HTML

<ul id="tasks">
    <li class="open">AAAAA</li>
    <li class="open">BBBBB</li>
    <li class="open">CCCCC</li>
</ul>

CSS

.open {text-decoration: underline;}

.closed {text-decoration: line-through;}

JS

function doTask(event) {
    event.target.classList.remove("open");
    event.target.classList.add("closed");
}

function undoTask(event) {
    event.target.classList.remove("closed");
    event.target.classList.add("open");
}


function setup () {
    document.querySelectorAll("li.open")[0].addEventListener("click", doTask);
    document.querySelectorAll("li.open")[1].addEventListener("click", doTask);
    document.querySelectorAll("li.open")[2].addEventListener("click", doTask);
    document.querySelectorAll("li.closed")[0].addEventListener("click", undoTask);
    document.querySelectorAll("li.closed")[1].addEventListener("click", undoTask);
    document.querySelectorAll("li.closed")[2].addEventListener("click", undoTask);}

window.addEventListener("load", setup);

In this part of code

document.querySelectorAll("li.closed")[0].addEventListener("click", undoTask);
document.querySelectorAll("li.closed")[1].addEventListener("click", undoTask);
document.querySelectorAll("li.closed")[2].addEventListener("click", undoTask);

you are trying to add click function to li.closed , but there isn't any li.closed until you clicked it. You can create another function ( doSomething in the example below) for checking the target class, then doTask or undoTask depends on the contained class, like this

 function doTask(event) { event.target.classList.remove("open"); event.target.classList.add("closed"); } function undoTask(event) { event.target.classList.remove("closed"); event.target.classList.add("open"); } function doSomething(event){ if (event.target.classList.contains('open')){ doTask(event); } else { undoTask(event); } } function setup () { document.querySelectorAll("li")[0].addEventListener("click", doSomething); document.querySelectorAll("li")[1].addEventListener("click", doSomething); document.querySelectorAll("li")[2].addEventListener("click", doSomething); } window.addEventListener("load", setup); 
 .open {text-decoration: underline;} .closed {text-decoration: line-through;} 
 <ul id="tasks"> <li class="open">AAAAA</li> <li class="open">BBBBB</li> <li class="open">CCCCC</li> </ul> 

That is because for lines like this document.querySelectorAll("li.closed")[0].addEventListener("click", undoTask); . It is trying to add event to elements which does not exist at all.

Infact you may not need a separate function to remove the class.

Use classList.toggle method to toggle the class

 function doTask(event) { event.target.classList.toggle("closed"); } function setup() { document.querySelectorAll("li")[0].addEventListener("click", doTask); document.querySelectorAll("li")[1].addEventListener("click", doTask); document.querySelectorAll("li")[2].addEventListener("click", doTask); } window.addEventListener("load", setup); 
 .open { text-decoration: underline; } .closed { text-decoration: line-through; } 
 <ul id="tasks"> <li class="open">AAAAA</li> <li class="open">BBBBB</li> <li class="open">CCCCC</li> </ul> 

The problem is that you are adding event listeners to these elements in your setup() function:

document.querySelectorAll("li.closed")[0]
document.querySelectorAll("li.closed")[1]
document.querySelectorAll("li.closed")[2]

Those elements do not exist yet when you execute setup() . That's why when clicking on a <li> element with class="closed" , it won't work.

The solution is to check the class of that specific element after clicking it. Like so:

 function onLiClick(event) { if (event.target.className === 'open') event.target.className = 'closed'; else event.target.className = 'open'; } function setup() { for (var li of document.querySelectorAll('li')) li.addEventListener('click', onLiClick); } setup(); 
 .open::after { content: "open"; } .closed::after { content: "closed"; } 
 <ul> <li class="open"></li> <li class="open"></li> <li class="open"></li> </ul> 

Try like this

function setup () {
    document.querySelectorAll("li.open")[0].addEventListener("click", doTask);
    document.querySelectorAll("li.open")[1].addEventListener("click", doTask);
    document.querySelectorAll("li.open")[2].addEventListener("click", doTask);
}

    function doTask(event) {
        if (!event.target.classList.contains("open")) {
            undoTask(event);
        } else {
                    event.target.classList.remove("open");
                event.target.classList.add("closed");    
        }
    }

    function undoTask(event) {
        event.target.classList.remove("closed");
        event.target.classList.add("open");
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM