0 votes
by (160 points)

I tried to implement an progress bar in my Sugarcube Story. With Sugarcube 2.21.0 everything worked fine, but after an update to Sugarcube 2.24.0 it seems like the script is running before registering the "id". Is there any way to fix it?

Passage Code:

<<set _a to 43>>
<<set _c to Math.round(_a/10)>>
<<nobr>><ul id="loadbar">
    <li>
    <div class="layerFill1" id="1"></div>
    </li>
    <li>
    <div class="layerFill2" id="2"></div>
    </li>
    <li>
    <div class="layerFill3" id="3"></div>
    </li>
    <li>
    <div class="layerFill4" id="4"></div>
    </li>
    <li>
    <div class="layerFill5" id="5"></div>
    </li>
    <li>
    <div class="layerFill6" id="6"></div>
    </li>
    <li>
    <div class="layerFill7" id="7"></div>
    </li>
    <li>
    <div class="layerFill8" id="8"></div>
    </li>
    <li>
    <div class="layerFill9" id="9"></div>
    </li>
    <li>
    <div class="layerFill10" id="10"></div>
    </li>
</ul><</nobr>>
<<for _i to 1; _i lt _c+1; _i++>>
<<script>>
	window.progresstest();
<</script>>
<</for>>

 Javascript Code:

window.progresstest = function () {
    var b='_b';
    var d='_i';
	document.getElementById(d).style.width="100%";
	document.getElementById(d).style.height="12px";
}	

Errormessage: <<script>>: bad evaluation: Cannot read property 'style' of null

1 Answer

0 votes
by (160 points)

Found a solution myself with $(document).on(':passagedisplay', function (ev) {

<<set _a to 43>>
<<nobr>><ul id="loadbar">
    <li>
    <div class="layerFill1" id="1"></div>
    </li>
    <li>
    <div class="layerFill2" id="2"></div>
    </li>
    <li>
    <div class="layerFill3" id="3"></div>
    </li>
    <li>
    <div class="layerFill4" id="4"></div>
    </li>
    <li>
    <div class="layerFill5" id="5"></div>
    </li>
    <li>
    <div class="layerFill6" id="6"></div>
    </li>
    <li>
    <div class="layerFill7" id="7"></div>
    </li>
    <li>
    <div class="layerFill8" id="8"></div>
    </li>
    <li>
    <div class="layerFill9" id="9"></div>
    </li>
    <li>
    <div class="layerFill10" id="10"></div>
    </li>
</ul><</nobr>>
<<set _c to Math.round(_a/10)>>
<<if _c lte 1>>
	<<set _b to "linear-gradient(to bottom, rgba(230,5,15,1) 0%,rgba(229,114,118,1) 100%)">>
<<elseif _c gt 1 and _c lte 2>>
	<<set _b to "linear-gradient(to bottom, rgba(224,21,175,1) 0%,rgba(221,110,194,1) 100%)">>
<<elseif _c gt 2 and _c lte 3>>
	<<set _b to "linear-gradient(to bottom, rgba(199,28,222,1) 0%,rgba(207,110,221,1) 100%)">>
<<elseif _c gt 3 and _c lte 4>>
	<<set _b to "linear-gradient(to bottom, rgba(72,43,216,1) 0%,rgba(124,107,214,1) 100%)">>
<<elseif _c gt 4 and _c lte 5>>
	<<set _b to "linear-gradient(to bottom, rgba(50,80,213,1) 0%,rgba(118,137,211,1) 100%)">>
<<elseif _c gt 5 and _c lte 6>>
	<<set _b to "linear-gradient(to bottom, rgba(56,140,210,1) 0%,rgba(169,191,209,1) 100%)">>
<<elseif _c gt 6 and _c lte 7>>
	<<set _b to "linear-gradient(to bottom, rgba(63,193,207,1) 0%,rgba(167,203,206,1) 100%)">>
<<elseif _c gt 7 and _c lte 8>>
	<<set _b to "linear-gradient(to bottom, rgba(69,205,169,1) 0%,rgba(171,204,195,1) 100%)">>
<<elseif _c gt 8 and _c lte 9>>
	<<set _b to "linear-gradient(to bottom, rgba(75,202,124,1) 0%,rgba(171,201,182,1) 100%)">>
<<else>>
	<<set _b to "linear-gradient(to bottom, rgba(123,196,87,1) 0%,rgba(212,219,208,1) 100%)">>
<</if>>
<<for _i to 1; _i lt _c+1; _i++>>
<<set _d to _i>>_d
<<script>>
 var b = State.getVar("_b");
 var i = State.getVar("_i");
 $(document).on(':passagedisplay', function (ev) {
     	    document.getElementById(i).style.width="100%";
	        document.getElementById(i).style.height="12px";
	        document.getElementById(i).style.backgroundImage=b;
});
<</script>>
<</for>>

 

by (68.6k points)

The "since Sugarcube ver 2.24.0" in your question is misleading as the code you initially tried has never worked.  This is due to the fact that the rendering passage content is not yet on the page, which is why you have to delay the code until it is.

Beyond that, your new code has some issues.

  1. You're creating multiple persistent event handlers when it seems as though a single one-time-use handler would do.  Even if you did need a persistent handler, a single one would likely be sufficient.
  2. The State.getVar() method is most useful when you do not know one, or both, of a variable's name or whether it's a story or temporary variable beforehand.  Otherwise, you're better off using either State.variables or State.temporary as appropriate.
  3. You're not caching values that you're using repeatedly.  This is especially egregious in cases like calls to document.getElementById().

As a suggestion, I'd look into replacing your entire <<for>> loop with the following:

<<script>>
$(document).one(':passagedisplay', function (ev) {
	var bg  = State.temporary.b;
	var len = State.temporary.c + 1;

	for (var i = 1; i < len; i++) {
		var el = document.getElementById(i);
		el.style.width = '100%';
		el.style.height = '12px';
		el.style.backgroundImage = bg;
	}
});
<</script>>

If you really want to print out the IDs as well, and that wasn't simply for testing, then you could keep the <<for>> around and simply have it do that (do not put the above example <<script>> into the loop in that case, keep them separate).

...