0

I have a simple range input and I would like to return the value, every few seconds.

I created two callback functions func and step. I would like to create another callback function that gets called every x seconds from inside rangeListener

Any ideas?

edit: edited per comments.

class Test{
    rangeListener(range, func, step) {
        range.addEventListener("change", function () {
            func(range.value);
        });

        let listener = function () {
            window.requestAnimationFrame(function () {
                step(range.value);
            });
        };

        range.addEventListener("mousedown", function () {
            listener();
            range.addEventListener("mousemove", listener);
        });

        range.addEventListener("mouseup", function () {
            range.removeEventListener("mousemove", listener);
        });

        range.addEventListener("keydown", listener);
    }
}

let test = new Test();

test.rangeListener(document.querySelector(".range"), (value) => {

  document.querySelector(".value").innerHTML = value;

}, (step)=> {

  document.querySelector(".step").innerHTML = step;

});
<input type="range" class="range" min="1" max="100" value="1">

<div class="value">0</div>
<div class="step">0</div>
Patrioticcow
  • 26,422
  • 75
  • 217
  • 337
  • A little confused. "I would like to create another one that gets called every x seconds." Technically this has nothing to do with debouncing a function. – gforce301 Apr 11 '18 at 17:23
  • What's wrong with `setTimeout`? – Xotic750 Apr 11 '18 at 17:26
  • Well, when I drag the slider, I get every step value. I want to get it every few seconds. I think that's `debouncing `. but i could be wrong – Patrioticcow Apr 11 '18 at 17:27
  • Nothing wrong with `setTimeout`. I'm ok with a solution using it. I just know there is a way to not use that with `requestAnimationFrame ` – Patrioticcow Apr 11 '18 at 17:28
  • 1
    sounds like you want `throttle` not `debounce` Lodash has both of them https://lodash.com/docs/4.17.5#throttle – Xotic750 Apr 11 '18 at 17:28
  • @Xotic750 The Lodash method you link to requires to be given a number of milliseconds, so it can set a timeout (so, not really what OP is asking for). It's easy to set a flag for that purpose: https://jsfiddle.net/4xLymm5e/ That being said, for doing it _every x seconds_, he'll need a timer. – blex Apr 11 '18 at 17:48
  • `Nothing wrong with setTimeout` :) The main thing was to clarify what the OP actually wants as it is a little unclear. – Xotic750 Apr 11 '18 at 17:49
  • @blex can u add a timer to your example? – Patrioticcow Apr 11 '18 at 18:07
  • @Patrioticcow Does that suit your needs? https://jsfiddle.net/1fg6bj85/ – blex Apr 11 '18 at 18:30
  • So it's a `setTimeout` solution? :) – Xotic750 Apr 11 '18 at 18:40
  • 1
    @Xotic750 Haha, for the _interval_ part, yes, there's no other simple way. For the throttle part where he is using `requestAnimationFrame`, though, he can keep using it :) – blex Apr 11 '18 at 19:47

1 Answers1

0

The throttle part

An easy way to throttle function calls is to use a flag (boolean variable). It works like this:

  • Set the flag to false
  • In your throttled function, don't do anything if the flag is set to true
  • Otherwise, set it to true and execute your function
  • When it's done, reset the flag to false, to allow later calls

The interval part

If you want to execute a function every X seconds, your best choice is to use setTimeout or setInterval. However, there are multiple ways to do it, and depending on your usage, you might prefer using one or the other. For example, with this code:

setInterval(myFunc, 5000);

Your function will wait 5 seconds before being executed.

If you want the first execution to happen immediatly, you can do this (using an IIFE here):

(function myInterval(){
    myFunc();
    setTimeout(myInterval, 5000);
})();

The result, with your code

I adapted your code below to incorporate these changes:

class Test {
  rangeListener(range, options) {
    /*
     * This flag will be set to true when we are waiting
     * for the next AnimationFrame, and back to false
     */
    let throttling = false;
    let timer;

    /*
     * Functions
     */
    const changeListener = () => {
      if (!options.onChange) return;
      options.onChange(range.value);
    };

    const stepListener = () => {
      if (!options.onStep || throttling) return;
      throttling = true;

      window.requestAnimationFrame(() => {
        options.onStep(range.value);
        throttling = false;
      });
    };

    const intervalFunc = (isInitialization = false) => {
      if (!options.onInterval) return;
      options.onInterval(range.value);
      if (!isInitialization) {
        timer = setTimeout(intervalFunc, options.delay || 500);
      }
    };

    /*
     * Event listeners
     */
    range.addEventListener("change", changeListener);

    range.addEventListener("mousedown", () => {
      clearTimeout(timer);
      intervalFunc();
      stepListener();
      range.addEventListener("mousemove", stepListener);
    });

    range.addEventListener("mouseup", () => {
      // Execute it once more to get the last value
      intervalFunc();
      clearTimeout(timer);
      range.removeEventListener("mousemove", stepListener);
    });

    range.addEventListener("keydown", stepListener);

    /*
     * Values initialization
     */
    changeListener();
    stepListener();
    intervalFunc(true);
  }
}

let test = new Test();

const setHTML = (selector, value) => {
  document.querySelector(selector).innerHTML = value;
};

test.rangeListener(document.querySelector(".range"), {
  onChange: (value) => setHTML(".value", value),
  onStep: (value) => setHTML(".step", value),
  onInterval: (value) => setHTML(".delayed", value),
  delay: 300
});
<input type="range" class="range" min="1" max="100" value="1">

<div>onChange: <span class="value">0</span></div>
<div>onStep: <span class="step">0</span></div>
<div>onInterval: <span class="delayed">0</span></div>
blex
  • 24,941
  • 5
  • 39
  • 72
  • one thing I noticed is that `intervalFunc();` needs to be inside `range.addEventListener("mousedown"..` so the timer starts when I start dragging the slider. and then `clearTimeout(timeout);` inside the `changeListener` so that the timer stops when I stop dragging – Patrioticcow Apr 11 '18 at 20:58
  • @Patrioticcow I misunderstood your question, then. I thought you wanted to execute it every `x` seconds, independently from the fact that it's being dragged or not. I'll adapt it. – blex Apr 11 '18 at 21:00
  • I edited the code, it might be closer to what you actually want. Tell me otherwise – blex Apr 11 '18 at 21:29