Sugarcube Macro Include, script and postdisplay

Twine Version: 2.3.7
Story Format: Sugarcube 2.31.1

I have a form with checkboxes that some of them are disabled by the main script using a postdisplay function with a filter.

postdisplay["general"] = function () {
if (passage() == "Option") {
		if (document.getElementById("checkbox-optionenablecheat").disabled !== null) {
 	 		document.getElementById("checkbox-optionenablecheat").disabled = true;
		};
	};
};

There is also a script in the passage to control the focus.

<<script>>
	setTimeout(function(){
		var focuslinks = document.getElementById("checkbox-optionenablesound")
		if (focuslinks !== null) {
			focuslinks.focus();
		};
	}, 500);
<</script>>

Everything was working fine.

Recently I have decided to reuse that form in a new passage. I put the form and the timeout script in a third passage named StorySettings and put <<include “StorySettings” “div”>> in the original passage Option. The form displays correctly. The form also displays correctly in the new passage.

However, I then find my scripts no longer work. I suspected it was the passage() that failed and it turned out that it was not. When I find out the setTimeout script eased to work as well, I admitted defeat.

Any idea what happened?

Well, you’re using the depreciated postdisplay task, no error checking, plus an unreliable setTimeout() call, so the problem is probably somewhere there (likely the last one).

I’d recommend replacing the first chunk of code with this:

$(document).on(":passagerender", function (event) {         // Gets triggered on each passage.
	if (passage() === "Option") {                // Makes sure you're in the "Option" passage.
		var cb = $(event.content).find("#checkbox-optionenablecheat");  // Finds the checkbox.
		if (cb.length) {                                     // Make sure the checkbox exists.
			if (!cb.prop("disabled")) {               // Sees if the checkbox is not disabled.
				cb.prop("disabled", true);                           // Disables the checkbox.
			}
		}
	}
});

See the jQuery API documentation for details on the above code.

Then, in your passage, you’d use this code instead:

<<script>>
	$(document).one(":passagerender", function (event) {      // Gets triggered this passage.
		function setCBFocus () {
			cb = $("#checkbox-optionenablesound");          // Look for checkbox on the page.
			if (cb.length) {                                // Make sure the checkbox exists.
				cb.focus();                                     // Set focus on the checkbox.
			} else {                                             // Didn't find the checkbox.
				if (++tries < 100) {
					setTimeout(setCBFocus, 20);                // Try again, up to 100 tries.
				}
			}
		}

		var cb = $(event.content).find("#checkbox-optionenablesound"), tries = 0;
		if (cb.length) {                                    // Make sure the checkbox exists.
			setTimeout(setCBFocus, 20);  // Wait for checkbox to be rendered to the document.
		}
	});
<</script>>

You might also want to add this to your Stylesheet section:

label {
    padding: 2px 3px 1px 3px;
}
label:focus-within {
	outline: 1px dotted #eee;
}

and then set up your checkboxes like this:

<label><<checkbox "$optionenablecheat" false true>> Enable Cheat</label>
<label><<checkbox "$optionenablesound" false true>> Enable Sound</label>

That will allow people to click on the text next to the checkbox, plus it will show an outline around all of it when the checkbox has focus.

Enjoy! :grinning:

when a programmer gets stuck, well, he gets stuck.

it turns out that when I was manipulating with my new setting page, two instances of the same form shows up on the screen, while one of them was masked by a display:none. I solved the issue with a for loop only a few minutes ago.

Anyway, I didn’t pay attention to the postdisplay depreciated issue. some of the codes I used were strict from my previous work and jquery really isn’t my most handy tools. many thanks for bringing this issue to me.

setTimeout is some risky codes I know and thanks for your code. I probably need some time to learn the document.one:postrender. looks a lot more reliable to me.

For the css, I used this:

.passage input[type=checkbox]:focus+label {
	font-weight: bold;
}

to go with

<<checkbox "$system.flow.allhint" false true checked>><label for="checkbox-systemflowallhint"> abc</label>

Don’t know why label:focus-within doesn’t work in my story.

edit: label:focus-within worked in my code after I played around with it. I am not sure why it didn’t came to me when I last worked on it a few weeks ago. Another thanks for this.