Here is a slider custom component that accepts an arbitrary numbers of labels (as well as the other expected properties). Some of the code is adapted from the slider component in the Anvil library.
Special thanks to @stucork for the killer CSS assistance (original post is here).
<div class="range">
<input type="range" id="range">
<div class="ticks" id="ticks">
<!-- JS generates the spans here -->
</div>
</div>
JS for dynamically setting labels and ticks:
function set_tick_labels(comp, array, minval, maxval, step, value) {
// set attributes of range slider
//var range = document.getElementById("range")
var range = comp.v._anvil.element[0]
range.setAttribute('min', minval);
range.setAttribute('maxval', maxval);
range.setAttribute('step', step);
range.setAttribute('value', value);
// get the inner "ticks" div
//var ticks = document.getElementById("ticks");
var ticks = $(range).find("div").find("div");
// for item in label array:
array.forEach(function (item, index) {
// create a span
var span = document.createElement("SPAN");
// set text to label, set class, append
span.textContent = item;
span.setAttribute('class', 'tick');
ticks[0].appendChild(span)
});
}
Hmm, I just realized that when I put more than one of my slider component on the screen at once, I run into the problem of not being able to distinguish between components in JS.
I believe Iāve been down this road before according to these posts:
Thanks again to @stucork, I was able to pass the components themselves to JS and select them using some methods that are still quite mysterious to me.
For example (and you can see this in the above JS too),
I wanted to use:
var range = document.getElementById("range")
But that selected both slider (i.e., range) components on the screen, so I used the following techniques from the linked post above (I admit that I donāt fully understand how this works):
// select the anvil component that is passed to JS
var range = comp.v._anvil.element[0]
.
.
.
// get the inner "ticks" div
var ticks = $(range).find("div").find("div");
.
.
.
ticks[0].appendChild(span)
I just want to point this out in case Iām doing something horribly wrong. Some of it is not pretty but somehow it all works.
This looks good to me. Worth noting that the [0] gets the dom element and without the [0] you get the jquery element so you could change it to
// select the component as jquery
var range = comp.v._anvil.element
.
.
.
// get the inner "ticks" div
var ticks = range.find(".ticks");
.
.
.
Also worth noting that this is a total hack.
I believe itās in the pipeline for anvil to add a JavaScript method getElement that will be a documented/supported way to do the first line.
Now that itās open source maybe Iāll get round to making that change and submitting a pull request
Pseudo-pull request:
I moved the code initializing the sliderās min/max values etc. to the sliderās form_show (rather than the Main formās form_show in the demo) so that the behavior is as (I) expected when using the slider component elsewhere. https://anvil.works/build#clone:Y5LUOEM5XWUSFREF=JMXUOTMM47I5ZPIPFLQFQYEK
The other issues I had trying to use this today are to do with the formatting of the tick labels (using ālabelā loosely hereātheyāre not Anvil labels but rather something set with JavaScript in the Custom HTML).
When a label is too long so that it wraps to multiple lines (like āstrongly agreeā in the demo), Iād like those lines to be center- rather than left-aligned.
Iād also like to be able to increase the width allowed before a label wraps to another line. (I figured out how to move the first and last ticks away from the edge of the HTML form so that there is room for a wider label there, but I couldnāt see how to increase the width of the labels themselves.)
Are these things easy to do for someone who understands JS and CSS better than me?