sliderrescript

Trouble translating JavaScript to ReScript


I wanted to implement an interactive slider using ReScript, so I wanted to translate this JavaScript to ReScript (courtesy: https://www.w3schools.com/howto/howto_js_rangeslider.asp):

var slider = document.getElementById("myRange");
var output = document.getElementById("demo");
output.innerHTML = slider.value; // Display the default slider value

// Update the current slider value (each time you drag the slider handle)
slider.oninput = function() {
  output.innerHTML = this.value;
}

where the target HTML (slider.html) is:

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
.slidecontainer {
    width: 100%;

}

.slider {
    -webkit-appearance: none;
    width: 100%;
    height: 25px;
    background: #d3d3d3;
    outline: none;
    opacity: 0.7;
    -webkit-transition: .2s;
    transition: opacity .2s;

}

.slider:hover {
    opacity: 1;
}

.slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 25px;
    height: 25px;
    background: #04AA6D;
    cursor: pointer;

}

.slider::-moz-range-thumb {
    width: 25px;
    height: 25px;
    background: #04AA6D;
    cursor: pointer;

}
        </style>
    </head>
    <body>
        <div class="slidecontainer">
            <input type="range" min="1" max="100" value="50" class="slider" id="myRange">
            <p>Value: <span id="demo"></span></p>
        </div>

        <script src="./bundled.bs.js"></script>
    </body>
</html>

where the bundled.bs.js is a result of bundling the compiled .bs.js code with Browerify.

So far, I have the following piece of code written using the rescript-webapi:

open Webapi.Dom
open Belt.Option

@val external document: Document.t = "document"

let slider = getExn(Document.getElementById("myRange", document))
let output = getExn(Document.getElementById("demo", document))

Element.setInnerText(output, getExn(Element.getAttribute("value", slider)))

Element.addEventListener("value", _ => 
    Element.setInnerHTML(output, getExn(Element.getAttribute("value", slider))), slider)

However, this does not work as intended; I can see the slider and slide it around, but the input tag's value does not get updated:

the slider is there, but it does not update the value.

What is missing from my ReScript translation?

+Update:

@glennsl has provided me with this code below:

open Webapi.Dom
open Belt

let slider = document->Document.getElementById("myRange")->Option.getExn
let output = document->Document.getElementById("demo")->Option.getExn

output->Element.setInnerText(slider->Element.getAttribute("value")->Option.getExn)

slider->Element.addEventListener("input", _ => {
  let value = slider->Element.getAttribute("value")->Option.getExn
  output->Element.setInnerHTML(value)
})

I can see the Value: with the correct initial value, 50. the initial screen with Value: 50. However, even if I repositioned the slider, the value does not get updated:the Value is not updated with the repositiioned slider.

As mentioned above, I compiled this code (dubbed slider.res) into slider.bs.js and bundled it with browserify slider.bs.js -o bundled.bs.js.


Solution

  • Ugh, the main problem was twofold:

    1. As @glennsl pointed out, the event name was wrong. It should be "input", not "value".

    2. I mistook the this.value to refer to the value attribute of this, which is slider. Actually, this is considering slider to be an HTML input element, and referring to its input value.

    As we cannot directly cast an Dom.Element.t to an Dom.HtmlInputElement.t, we first convert this to Dom.Node.t and then to Dom.HtmlInputElement.t. So, building upon @glennsl's code, the code is:

    open Webapi.Dom
    open Belt
    
    let slider = document->Document.getElementById("myRange")->Option.getExn
    let output = document->Document.getElementById("demo")->Option.getExn
    
    output->Element.setInnerText(slider->Element.getAttribute("value")->Option.getExn)
    
    slider->Element.addEventListener("input", _ => {
      let value = slider->Element.asNode->HtmlInputElement.ofNode->Option.getExn->HtmlInputElement.value
      output->Element.setInnerHTML(value)
    })