Problem adding class to classList on window.onload

The problem I’m having is below the relevant code.

So I have this ‘movie’ list that is just a fancier to-do list. I’m saving the lists locally and it’s working fine. The lists save and load. I also have stamps that show that the movie is ‘approved’ by toggling a class on and off.

Here is the toggle function:

function approveMovie() {	
	if(this.parentElement.id === "listDanny") {
		this.children[1].innerText = "Lola approved!";
		this.children[1].classList.toggle('approvedVisible');
	}

	if(this.parentElement.id === "listLola") {
		this.children[1].innerText = "Danny approved!";
		this.children[1].classList.toggle('approvedVisible');
	}
}

And here are the classes I’m using. As you can see, to make the stamp visible I just toggle the .approvedVisible class on and off.

.approvedHidden {
  opacity: 0;
  position: absolute;
  left: 175px;
  top: -10px;
  font-size: 20px;
  color: green;
  transform: skew(0deg, 170deg);
}

.approvedVisible {
  opacity: 1;
}

And here are the save and load functions:

function saveFunk(state1, state2) {
		localStorage.setItem('dannyMovies', JSON.stringify(dannyArray));
		localStorage.setItem('lolaMovies', JSON.stringify(lolaArray));
		localStorage.setItem('dannyStates', JSON.stringify(state1));
		localStorage.setItem('lolaStates', JSON.stringify(state2));
		console.log('states: ', state1, state2);
	}

window.onload = function() {
	dannyArray = Array.from(JSON.parse(localStorage.getItem('dannyMovies')));
	lolaArray = Array.from(JSON.parse(localStorage.getItem('lolaMovies')));
	dannyArray.forEach(movie => addItem("Danny", movie));
	lolaArray.forEach(movie => addItem("Lola", movie));

	dannyStampStates = Array.from(JSON.parse(localStorage.getItem('dannyStates')));
	lolaStampStates = Array.from(JSON.parse(localStorage.getItem('lolaStates')));

	assignStamps(listDanny, dannyStampStates);	
	assignStamps(listLola, lolaStampStates);

	console.log('states2: ', dannyStampStates, lolaStampStates);
}

So…I have another function that is saving the states of the stamps, whether they are on or off, and am saving them in the saveFunk above(the StampStates). As you can see in the onload function, the saved lists are parsed and then re-added to the list. Then my stamp states are parsed and passed into the assignStamps function. The assignStamps function re-adds the approvedVisible class to each of the list items according to their previous saved states.

And this works. When I re-load the page, the lists are added like they should, and the classes are added but remain hidden. I can inspect each list item that is supposed to have the approvedVisible class, and they do have them. The problem is that they don’t appear. If I then click on a list item that has the class, the class gets toggled off. I click again, and the class is toggled on and appears.
So that’s my problem. How do I get the stamp to appear on load, even when the class is getting added like it should. I’m suspicious that it has something to do with the toggle, but I’m not sure.

Sorry for the long explanation for such a short problem :smiley:

1 Like

The explanation would be great, with a working(-ish) example. Do you have this in a codepen, repl, or jsfiddle? Somewhere we can break it and see what happens?

At a guess, you may want to increase the specificity of the .approvedVisible class. What’s happening, if I were to guess, is that, because it has the same level of specificity as .approvedHidden, it is being added, but promptly being overridden. As that is the class you want to override, simply change the CSS rule to be slightly more specific.

Here’s an idea: you aren’t removing the .approvedHidden class from the approved selections, so make the rule for the visible “an element that has both .approvedHidden and .approvedVisible classes”! How might you do that?

.approvedHidden.approvedVisible {
  opacity: 1;
}

By not having a space between the class names, it indicates an element with both classes assigned. And it’s more specific than .approvedHidden, so it will over-rule that one, every time.

1 Like

As snowmonkey mentioned, it would be helpful to see all the code. The code looks great, btw - nice and clean :slightly_smiling_face:

Based on what you did provide, my guess is how/when you are calling the load method and/or where the script is placed.

Check out the MDN docs for window:load and DOMContentLoaded. Again, based on what I am seeing here, my guess is you’ll want to move the code you are calling in the onload function to something like:

document.addEventListener('DOMContentLoaded', (event) => {
   dannyArray = Array.from(JSON.parse(localStorage.getItem('dannyMovies')));
	lolaArray = Array.from(JSON.parse(localStorage.getItem('lolaMovies')));
	dannyArray.forEach(movie => addItem("Danny", movie));
	lolaArray.forEach(movie => addItem("Lola", movie));
    //.....
  });
2 Likes

Thank you both for the great replies! As requested here’s a codepen: https://codepen.io/homtanks/pen/XWrgXXq
Be aware that I haven’t really messed with the styling much, so it’s not mobile responsive.

Go ahead and mess around with adding list items and clicking on the items to toggle the stamp. You’ll see in the console, when you press the save button, the current state of the stamps is logged and saved. And then you’ll see on reload that it logs the state of the stamps, which should be the same. And if you inspect the items you’ll see that approvedVisible is applied, and it shows it has opacity: 1 and opacity: 0

I’ll mess around with it later today and let you know what I find, thanks for the help!

EDIT: As a side note @j10wy, thank you for the compliment, and if possible I would love some feedback on the flow of the script in general. In my head I thought that it made sense that you could read it from the bottom up. Meaning, starting at the event listeners/onload functions at bottom, you would be able to follow the flow of what happens reading upwards. But I’m not sure really what the best practice is regarding the order of events.

So here’s an update. I made .approvedVisible identical to .approvedHidden other than the opacity.

.approvedHidden {
  opacity: 0;
  position: absolute;
  left: 175px;
  top: -10px;
  font-size: 20px;
  color: green;
  transform: skew(0deg, 170deg);
}

.approvedVisible {
  opacity: 1;
  position: absolute;
  left: 175px;
  top: -10px;
  font-size: 20px;
  color: green;
  transform: skew(0deg, 170deg);
}

And I’ve changed the approveMovie function so that it changes the className on click, from hidden to visible and vice versa:

function approveMovie() {	
	if(this.parentElement.id === "listDanny") {
		this.children[1].innerText = "Lola approved!";
		if(this.children[1].className ==='approvedHidden') {
			this.children[1].className = 'approvedVisible';
		} else {this.children[1].className = 'approvedHidden';}
		
	}

	if(this.parentElement.id === "listLola") {
		this.children[1].innerText = "Danny approved!";
		if(this.children[1].className ==='approvedHidden') {
			this.children[1].className = 'approvedVisible';
		} else {this.children[1].className = 'approvedHidden';}
	}
}

And lastly changed my assignStamp function to change the className to approvedVisible on load:

function assignStamps(list, stamps) {
	const items = Array.from(list.getElementsByTagName("div"))
	for(let i=0;i<items.length;i++) {
		if(stamps[i] === true) {
			items[i].className = 'approvedVisible';
		}
	}
}

And!..it’s the same problem. This works how I want it to, the lists are assigned approvedVisible on load when they are supposed to, and everything looks good in the console, but they still aren’t showing up. So @snowmonkey, it doesn’t seem to be an issue of specificity. I guess I’ll mess with the onload function a bit to see if that’s where the problem lies.

EDIT: I haven’t changed any of the Codepen code, it’s still how it was before i made these changes.