Have a slider output a variable

+1 vote
asked Sep 6 by RealityDev (1,130 points)
edited Sep 7 by RealityDev

I'm working on a slider input system, and having a bit of trouble. Hopefully some fine folks here can point me in the right direction! I'm using Twine 2 and Sugarcube 2.

Goals:

  1. Have a slider input that spits out a value.
    • I want to be able to use the output for various potential things: A measurement, a percentage, or even just a regular ol' number.
    • Ideally I just want it to give me a variable, rather than rendering the output into a span. (Is it possible to convert the contents of a span into a variable?)
  2. Have a 'partnered' text input box.
    • This box should be prefilled with a 'live' update of the slider's value
    • If a user enters a value, the slider should adjust accordingly
      • If an input value is out of the slider's range, it will snap to the appropriate min/max value
      • Bonus points for having the slider animate into position when needed. Some CSS transition magic, perhaps?
      • Bonus points for this being set up to update on keyrelease or some such, to make the slider respond to the textbox 'live'
    • User input should be limited to numbers
      • Bonus points if they can enter decimals (ie 1.8m, which would be stored as 180)
  3. Have this as a macro, so that it's not necessary to have a bunch of <script> wherever the functionality is needed.

What I've tried so far:

I started out by playing around with the slider in the <<numberpool>> macro set, found at the bottom of this page. This is really nice and all, but for the life of me I couldn't figure out how to use the output value quite in the ways I want, such as displaying it as a measurement, ie 1.7m.
I tried combining it with the <<numberpool>> and <<onchange>> macros from the set and managed to get a textbox updating when the user releases the slider handle, which is a start. Here's my code:

<<set $defaultHeight = "170">>

<<numberpool "$height">>
<<numberslider "$tempHeight" $defaultHeight 140 200 1>>
<<onchange>>
	<<replace "#heightSpan">><<textbox "$playerHeight" $tempHeight>><</replace>>
<</numberpool>>

You are <span id="heightSpan"><<textbox "$playerHeight" $defaultHeight>></span>cm tall.

The firstmost issue with this is the error that gets thrown up after the closing <</numberpool>>:

Error: cannot execute macro <<numberslider>>: <<replace>>: no elements matched the selector "#heightSpan"

I'm not sure I'm understanding the cause for this error, as it seems to perform as expected.
Regardless, this solution has some issues:

  1. I haven't figured out how to remove/hide the slider output from its default position next to the slider. Ideally I'd like for the linked <<textbox>> to sit there instead. [EDIT - currently achieving this with a display: none; in the CSS for the appropriate span]
    • Nor can I figure out how to style or otherwise present the output (ie 1.7 meters)
  2. The reverse functionality I'm after isn't quite there (though I'm still hacking some solution together for that). Currently I have an "update slider" link which re-renders the slicer chunk of code with an updated $defaultHeight that does the job of updating the slider, but I'd rather this happens when the user either hits enter, or deselects the textbox.
  3. The textbox doesn't update 'live'.

 

Alternatively, I've also experimented with a javascript approach using the following foundation:

<input type="range" value=0 min=0 max=10 id="rangevalue"> <span id="text">0</span>

<script>
$("#rangevalue").mousemove(function () {
    $("#text").html($("#rangevalue").val())
})
</script>

Works like a charm in a vaccuum, however I run into the same issue where I don't know how to turn the contents of that <span id="text"> into a variable that I can track and make proper use of. Also no idea where I'd start as far as making the slider respond to the text input.

 

I feel like I must be close, but the final pieces of this puzzle elude me. Hopefully I've laid out this post thoroughly enough!

- EDIT -

A thought - maybe I can figure out how to reverse engineer the <<onchange>> macro enough to make a similar one that updates on mouse move, or some such...

- EDIT 2 -

A further thought, <<onchange>> shouldn't be needed at all if I can just figure out how to make the <<numberslider>>'s variable update the moment the value changes, rather than when the mouse is released...

2 Answers

+1 vote
answered Sep 7 by TheMadExile (18,970 points)
selected Sep 8 by RealityDev
 
Best answer

Ideally I just want it to give me a variable, rather than rendering the output into a span. (Is it possible to convert the contents of a span into a variable?)

The <span id="numberslider-value-{varNameSlug}"> element is simply the part of the UI which shows the actual value, because the slider alone doesn't tell players much.  It's completely separate from the story or temporary variable you've already specified (the first argument to the macro) which will be set by the macro to value you want, you don't need to convert the contents of the element for anything.

Beyond that, I'm unsure why your attempting to use a <<textbox>> in addition to <<numberslider>> in the first place.  Also, $defaultHeight should be a number, not a string.

Have you tried something like the following?

<<set $defaultHeight to 170>>\
<<numberpool "$height">>
	<<numberslider "$playerHeight" $defaultHeight 140 200 1>>
<<onchange>>
	<<replace "#playerHeight">>$playerHeight<</replace>>
<</numberpool>>

You are <span id="playerHeight">$playerHeight</span>cm tall.

 

Additionally.  If you want to add some kind of unit to the value shown in the <span id="numberslider-value-{varNameSlug}"> element, then you could do so with a ::after style rule using the content property.  For example:

/* Replace `{varNameSlug}` below with the appropriate value. */
#numberslider-value-{varNameSlug}::after {
	content: '\00a0cm';
}

 

commented Sep 7 by RealityDev (1,130 points)
edited Sep 7 by RealityDev

Thanks for the response!

I did actually finally stumble onto the solution suggested, which is certainly better than what I had when I made the post. Thanks for putting that there! The more I fiddle with this, the more I'm getting my head around it (as it should be!) Duly noted re: the $defaultHeight number VS string. Overlooked that.
EDIT: I would note that your solution still throws up the error I mentioned.

I've also been experimenting with ChapelR's event macro and the mousemove JS event.

In my ideal world, I'd like for a player to be able to input their desired value using either a slider OR via a direct text input. Secondarily, I kind of relish the challenge.

I'm trying to figure out what makes the <<numberslider>> JS tick - is there a way to set it up so that $playerHeight dynamically updates to match the <span id="numberslider-value-{varNameSlug}"> element in live time (rather than when the user releases the slider handle)?

TL;DR:

  1. Want the user to be able to use slider or textbox, just because.
  2. Want the $playerHeight to be updated as the user drags the slider handle
commented Sep 7 by TheMadExile (18,970 points)
edited Sep 8 by TheMadExile

EDIT: I've published a new release of the <<numberpool>> macro set, which should address one of your wants.

 

EDIT: I would note that your solution still throws up the error I mentioned.

Changing the example thus should fix the problem.

<<set $playerHeight to 170>>\
<<numberpool "$height">>
	<<numberslider "$playerHeight" $playerHeight 140 200 1>>
<<onchange>>
	<<replace "#playerHeight">>$playerHeight<</replace>>
<</numberpool>>

EDIT: There was also some bleed-through from the <<onchange>> which shouldn't have been happening, which should be fixed in the new release.

 

  1. Want the user to be able to use slider or textbox, just because.

  2. Want the $playerHeight to be updated as the user drags the slider handle

As the <<numberpool>> macros are currently written, doing the former would be problematic and the latter impossible.  The former because synchronizing the two separate input elements without also synchronizing how changes to them are validated and applied is non-trivial.  The latter because the validation and application of slider value changes happens only when the change event is fired, which only happens when you release the control.  EDIT: I couldn't think of a good reason why slider changes couldn't be made immediately to the variables involved, so I've updated the <<numberslider>> to do just that—hopefully that doesn't bite me in the arse.

If having a synchronized text entry box, in addition to the slider, is a must have, then you may be better off rolling your own custom solution.

commented Sep 8 by RealityDev (1,130 points)
edited Sep 8 by RealityDev

Hot dawg! That's some exciting stuff!

However, I'm afraid that now with the code snippet that follows, I get this error:

<<set $playerHeight to 170>>\

<<numberpool "$height">>
	<<numberslider "$playerHeight" $playerHeight 140 200 1>>
<<onchange>>
	<<replace "#playerHeight">>$playerHeight<</replace>>
<</numberpool>>

You are <span id="playerHeight">$playerHeight</span>cm tall.
Error: <<numberpool>>: pool value is not a number

I certainly appreciate that you've obviously given this an amount of thought and effort already, but do you have any further thoughts on what might be going wrong here?

- EDIT - 

Some more experimentation, and the following works with some caveats that if you have any insight, I'd love to understand more clearly.

<<set $playerHeight = 170, $height = 200>>\

<<numberpool "$height">>
	<<numberslider "$playerHeight" $playerHeight 140 200 1>>
<<onchange>>
	<<replace "#playerHeight">>$playerHeight<</replace>>
<</numberpool>>

You are <span id="playerHeight">$playerHeight</span>cm tall.

So, ensuring that one sets $height solves the error and everything seems to function quite happily.

However, the value of $height DOES seem to dictate how many 'points' the user has to use in the pool. Which makes sense in a way. If it is set to 0, then the slider cannot be moved further than the set $playerHeight value. If it is set to 10, then the maximum value is $playerHeight + 10.

In order to ensure that the 'full slider' is available, I just stuck 200 in there (the same as what I set for the slider's max value).

Is there a trick to this to just have it 'work' without worrying about setting the <<numberpool>>'s variable manually? No big deal if not, just wondering.

I figured this out by also playing with the <<onchange>> documentation in the readme file, which also threw up errors in its default, copy/paste state. - if there's some new requirements for these to work, maybe that could be updated to reflect this? 

commented Sep 8 by TheMadExile (18,970 points)
edited Sep 8 by TheMadExile

The pool variable must exist and be defined, its value must be a number and is number of "points" available within the pool.  The pool acts as a buffer and clamp for the other two macros in the set, when used as part of the pool.

The reason you were seeing errors initially was because you never defined the pool variable.  It never occurred to me that anyone would fail to understand that you need to specify the pool, so the macro never specifically checked for it—that's what caused the 'cannot execute macro <<numberslider>>' part of the error.  Also, as mentioned previously, there was some bleed-through from the <<onchange>> section which I hadn't noticed because I'd never tested without a defined pool—that's where the '<<replace>>: no elements matched the selector "#heightSpan"' part of the error came from.  Both of those issues were fixed in the release I published—which is why you received the '<<numberpool>>: pool value is not a number' error after the update.

Frankly, you shouldn't be using <<numberpool>> unless you really want to define a pool.  Using it simply for its <<onchange>> is not how it's intended to be used.  So, yes, if you intend to use it, you will have to define a pool value.

TIP: For the pool variable, and any other variable whose value doesn't need to be remembered, I would suggest using a temporary variable, rather than a story variable—e.g. _height vs $height.  See TwineScript > Variables for more information.

commented Sep 8 by RealityDev (1,130 points)

Got it. Thanks for explaining that to me - I HAD been using <<numberpool>> in order to make use of <<onchange>>, because it seemed to be to be the simplest way to accomplish updating another element the moment the slider updated. Though I had also been experimenting with ChapelR's event macro and JQuery events in order to get the same result, <<onchange>> still seemed a more direct way to do this.

Thank you very much for your time, effort and for humouring me in all this! I'm marking this as well and truly answered, though if you would humour me just once more I'd be keen to know what you would suggest as best practice in place of the <<onchange>> event.

Feels good to be learning!

0 votes
answered Sep 7 by Liamw104 (2,740 points)

This is some html code, It may not fully work though.

<input type="range" id="myRange" value="90">

<p>Click the button to get the value of the slider control.</p>

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

<p><strong>Note:</strong> input elements with type="range" are not supported in IE 9 and earlier versions.</p>

<p id="demo"></p>

<script>
function myFunction() {
    var x = document.getElementById("myRange").value;
    document.getElementById("demo").innerHTML = x;
}
</script>

 

commented Sep 7 by Liamw104 (2,740 points)

This is the only thing that matters.

<input type="range" id="myRange" value="90">

 

commented Sep 8 by RealityDev (1,130 points)
Not quite what I was after, but thank you for your time and effort - I appreciate it!
Welcome to Twine Q&A, where you can ask questions and receive answers from other members of the community.

You can also find hints and information on Twine on the official wiki and the old forums archive.

See a spam question? Flag it instead of downvoting. A question flagged enough times will automatically be hidden while moderators review it.
...