0

I have the following code executing from a click event listener function

if (target.tagName === 'LABEL'){
    
    if (target.classList.contains('active')){
        console.log('removing')
        target.classList.remove('active')
    }

    else {
        console.log('adding')
        console.log(target.classList)
        target.classList.add("active");
        console.log(target.classList)
    }
}

In both Chrome and Firefox, The issue is that it's not actually adding the class and I can't figure out why.

This same code works elsewhere, but in this one particular section it's not working.

Here is what I get from the console

click registered 

LABEL 

adding 

DOMTokenList(3)
​
0: "topic-label"
​
1: "btn"
​
2: "initialized"
​
length: 3
​
value: "topic-label btn initialized"
​
<prototype>: DOMTokenListPrototype { item: item(), contains: contains(), add: add(), … }

DOMTokenList(4)
​
0: "topic-label"
​
1: "btn"
​
2: "initialized"
​
length: 3
​
value: "topic-label btn initialized"
​
<prototype>: DOMTokenListPrototype { item: item(), contains: contains(), add: add(), … }

minimizing the DOMTokenList I see the following


click registered 

LABEL 

adding 

DOMTokenList(3) [ "topic-label", "btn", "initialized" ]


DOMTokenList(4) [ "topic-label", "btn", "initialized", "active" ]


I can see that I am properly getting to that area of my code, and I can see that the DomTokenList changes - but the actual classList is not changing.

I don't know how to even begin googling this, so maybe someone knows what's happening?

Thanks in advance. RR

More details

Here is the full event listener method

function initialize_topics(){

    var all_topics = byClass('topic-label')
    
    for (var i=0; i< all_topics.length; i++){
        
        if (all_topics[i].classList.contains('initialized')){
            continue
        }
        else{
            all_topics[i].classList.add('initialized');
            all_topics[i].addEventListener("click", function(e){
                //console.log('click registered')
                e=e || window.event;
                var target = e.target || e.srcElement;
               
                //console.log(target.tagName)        

                
                if (target.tagName === "LABEL"){
                    /*
                    if (target.classList.contains('active')){
                        console.log('removing')
                        target.classList.remove('active')
                    }

                    else {
                        console.log('adding')
                        console.log(target.classList)
                        target.classList.add("active");
                        console.log(target.classList)
                    }
                    */
                    console.log(target.firstElementChild.value)
                    console.log(target.classList)
                    target.classList.toggle('active')
                    console.log(target.classList)

                }
            });

        }
        
    }

}

This is the HTML of what I'm targeting here

<div class="lurnby_data_group topics-group" data-toggle="buttons">
   <h6 class="inline-block">Topics:</h6>
   <span  id="CreateTopic">
       <button onclick ="NewTopic()" class="main-button save">
            Create new topic
       </button>
   </span><br>
   <div class = "topics-all">
      {% for topic in topics %}
      <label class="topic-label btn">
         <input name = "topics" type="checkbox" value="{{topic.title}}">
         {{topic.title}}
      </label>
      {% endfor %}
      <span id = "topicplaceholder"></span>
   </div>
</div>

It's also not working in Chrome it seems. Here's a small vid where you can see what's happening a bit more.

https://www.loom.com/share/12703437f9a04d4e9ef44d3c933353de

And here is what I'm getting in the console now. I am first logging the value of the input, then the classlist, then toggling 'active', then logging the classlist again.

The issue is that the change is not actually being pushed to the DOM.

test 
DOMTokenList(3) [ "topic-label", "btn", "initialized" ]

DOMTokenList(4) [ "topic-label", "btn", "initialized", "active" ]

hellop 
DOMTokenList(3) [ "topic-label", "btn", "initialized" ]

DOMTokenList(4) [ "topic-label", "btn", "initialized", "active" ]

And all the elements have the listener attached as well -

screenshot from console.

Another Update

It seems that the issue might be with my repeated use of the function call which sets the event listener. But I don't understand why that would cause the issue, because I am not actually setting the event listener again.

The first time a label gets the event listener added, I give it a class called 'initialized' - anytime the function is called, it checks for that class first and if it doesn't exist, it adds the event listener.

I do this because I am also using the same code to initialize similar labels in an ajax loaded modal elsewhere in my app.

I don't understand why this would cause some kind of conflict.

Any ideas?

Roznoshchik
  • 95
  • 1
  • 9
  • Instead of `console.log(target.classList)`, try `console.log(target.matches(".active"))`. The console API can do weird things. – Pointy Oct 01 '20 at 13:21
  • `classList` itself is [read-only](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList). Also, it looks like you can use the `toggle()` function for the behaviour you're looking for, in place of you manually adding and removing the class. – John H Oct 01 '20 at 13:21
  • classList.toggle() is indeed a better approach thanks. But it's still not working as expected either. The 'active' class isn't actually being added to the label being clicked on. – Roznoshchik Oct 01 '20 at 13:58
  • Your code works fine for me. https://jsfiddle.net/gbe50nrs/ – John H Oct 01 '20 at 14:12
  • Use `let` instead of `var` here: `for (var i=0; i< all_topics.length; i++){` – connexo Oct 01 '20 at 19:41
  • @connexo , what does that change? Is it a general best practice to use let instead of var in such situations? – Roznoshchik Oct 01 '20 at 19:59
  • https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – connexo Oct 01 '20 at 20:17

2 Answers2

0

Though classList is read-only, it can be updated using the add(), remove() and toggle() methods.

The add() and remove() just works fine in the snippet. Are you checking in some online editor(like jsfiddle, etc) ?

Note: classList is not support in IE9 and older browsers.

<!DOCTYPE html>
<html>
<head>
<style>
.active {
  background-color: coral;
}
</style>
</head>
<body>

<button onclick="myFunction()">Try it</button>

<label id="myDIV">
I am a Label element
</label>

<script>
function myFunction() {
  
  let elem = document.getElementById("myDIV");
  if(elem.classList.contains("active"))
  {
    elem.classList.remove("active")
  }
  else
  {
    elem.classList.add("active");
  }
}
</script>

</body>
</html>
RPDP
  • 1,713
  • 1
  • 11
  • 6
  • I am not checking in a snippet. I am checking on my site live where the add() and toggle() methods aren't working. Rather, they are working sometimes and at other times they are not. I've added the console.log statements to see if I'm getting to the section of my functions and I am, but the add() and toggle() methods aren't working therein. What might be the reason for such behaviour? – Roznoshchik Oct 01 '20 at 14:54
  • Does it happen in any specific browser, or in all browsers you see the same behavior? – RPDP Oct 01 '20 at 15:47
  • It would be helpful if you can add more details on the DOM structure you are trying to process and the full event listener method to see further. – RPDP Oct 01 '20 at 15:56
  • It's currently happening in firefox. I will add the full event listener method in a second. Not sure what you mean with the dom structure I'm trying to process. But will add something too. – Roznoshchik Oct 01 '20 at 16:32
  • By DOM structure, I meant HTML code. Based on your update, Try the following: 1) check if any other part of your code is accessing this element and resetting it. 2). extract the event handler into a named function. like `function labelClickHandler(e) {....}` and update in the `all_topics[i].addEventListener("click", labelClickHandler)`. This is better to have a single common instance of event handler than multiple anonymous functions. – RPDP Oct 02 '20 at 01:24
  • thanks for the reply, I've been a bit slammed. Once I figured out what seemed to be causing the issue (see the latest update) I just created another identical function with a different name that targets a different class. I had the same initial function being used a few times because I have different modals that show those labels. The problem disappeared, but I still don't understand what was causing it. So I chose an inelegant solution just to move forward. re - making a named function - i tried that previously, but then kept getting errors that `e` is undefined. – Roznoshchik Oct 07 '20 at 09:01
0

This happened to me too and I have no idea why. My solution was to not use classList for adding and removing classes, but instead use className to add and remove classes. It is a workaround but it worked for me.

if (target.tagName === 'LABEL'){
    
    if (target.classList.contains('active')){
        console.log('removing')
        target.className = 'topic-label btn initialized';
    }

    else {
        console.log('adding')
        console.log(target.classList)
        target.className = 'topic-label btn initialized active';
        console.log(target.classList)
    }
}
Cade
  • 35
  • 5