Error: Cannot read properties of undefined (reading 'remove') When using .classList.remove

Twine Version: 2.3.16
Story Format: Sugarcube 2.36.1

So I’m trying to make a gallery that the player can scroll through. I’ve already set up the pictures and everything, but right now, I’m trying to make it so when the page is turned, the pictures on the first page disappear, and the second page appears. To that end, I made it so when the variable for the page number, gPN = 1, the second page of pictures has the class Hidden, which sets their opacity to 0. Alternatively, when gPN = 2, the Hidden class is removed, and added to the images from the first page. Here is the code.

if (!window.PC){
  window.PC = {};
}
PC.PageSwitch = function (dir) {
		var image1 = document.getElementsByClassName("galimg1")
		var image2 = document.getElementsByClassName("galimg2")
		if (dir === 1) {
				variables().gPN - 1
		}
		else if (dir === 2) {
				variables().gPN + 1
		}
		if (variables().gPN <= 0) {
				variables().gPN = 1
		}
		//Page Class Modifiers
		if (variables().gPN === 2) {
				image1.classList.add('Hidden');
				image2.classList.remove('Hidden');
		}
		else if (variables().gPN === 1) {
				image1.classList.remove('Hidden');
				image2.classList.add('Hidden');
		}
};

My issue is that when I press the next button, I get the following error.

Error: Cannot read properties of undefined (reading 'remove')

I checked to make sure everything was kosher, in case something had mismatching capitalization, but everything is matching. I don’t know how to fix this.

Most likely there are no elements on the page with those classes when you call that function, thus the classList parameter on those variables is undefined.

Remember, SugarCube first renders the passage to a “virtual” context prior to adding the passage to the document. Thus, if you’re executing the above code immediately when the passage is run, document.getElementsByClassName() won’t be able to find any of the elements in that passage, since the passage’s HTML isn’t in the document yet.

I’d recommend calling your PageSwitch function either from within a <<done>> macro or from code that goes something like this in that passage:

<<script>>
	$(document).one(':passagerender', function (event) {
		setup.PageSwitch(State.variables.dir, event.content);
	});
<</script>>

and in that case, instead of document, you’d want to use the event.content passed to the function to target those HTML elements.

Also, instead of using window.PC, I’d recommend using the SugarCube setup object, since it exists precisely so you can use it like that (note that I used it in the above code). And instead of doing variables().gPN, you should be doing State.variables.gPN to access SugarCube story variables (see the State.variables object).

So, the start of your PageSwitch function should probably look like this instead:

setup.PageSwitch = function (dir, context) {
	if (!context) { context = document; }
	var image1 = context.getElementsByClassName("galimg1")
	var image2 = context.getElementsByClassName("galimg2")

That will use the document object unless another context is passed to it, such as in the earlier code above.

Also, you might want to look into using jQuery (which is included with SugarCube), since it would allow you to turn this:

document.getElementsByClassName("galimg1").classList.remove('Hidden');
 - or -
context.getElementsByClassName("galimg2").classList.remove('Hidden');

into just this:

$(".galimg1").removeClass("Hidden");
 - or -
$(context).find(".galimg2").removeClass("Hidden");

Hope that helps! :slight_smile: