0

I'd like to be able to have a function attached to an element which will only run once a click has been held on that element for a given amount of time.

There are several (1, 2, 3) questions relating to handling mouse holds in javascript; but these questions either use jQuery or relate to a more particular use case.

I'd like to implement something more general, and I feel like there ought to be a good answer on stack overflow for this problem.

OliverRadini
  • 6,238
  • 1
  • 21
  • 46
  • (https://stackoverflow.com/questions/4158847/how-to-simulate-key-presses-or-a-click-with-javascript) – Ryan Wilson May 15 '19 at 12:37
  • @RyanWilson I think I'm missing something; that question relates to simulating key presses, is there anything related to handling mouse holds in there? – OliverRadini May 15 '19 at 12:38
  • As your code snippet is working, you should ask your question in the [Code Review SE community](https://codereview.stackexchange.com/). – Kévin Bibollet May 15 '19 at 12:44
  • I think your implementation is ok, I don't think there's any shorter or built-in version – Christian Vincenzo Traina May 15 '19 at 12:45
  • In order to implement a hold, you need to listen for mouse down and time how long it is before a mouse up. That's exactly what your code does, and quite tidily besides. Maybe you can just inline the `removeUpListener` call since that's a one-liner that is only called in one place, but realistically there's not much else to improve. – Niet the Dark Absol May 15 '19 at 12:46
  • Maybe use `Date.now()` instead of `new Date()` as well. – Niet the Dark Absol May 15 '19 at 12:47
  • @KévinBibollet I think you may be right that this is perhaps better on code review. I suppose there are three options: `1.` Remove the code from my question and add it as an answer. `2.` Ask the question on code review. `3.` Delete this question and answer one of the old questions. I have a bit of an aversion to the Code Review SE, and as I say, the old questions aren't really ideal either. I could delete the code from here and post it as an answer? – OliverRadini May 15 '19 at 12:50
  • @NiettheDarkAbsol thank you for your input; I think you're right about both of those, I may move my code to an answer in order to better fit with how SO ought to work – OliverRadini May 15 '19 at 12:50
  • @RyanWilson Sorry, I'm not sure I follow; I'm not looking to simulate a key hold, but to respond to one. – OliverRadini May 15 '19 at 12:52
  • @RyanWilson thanks for the suggestion; but that question's answer relies on jQuery, which is something I'd like to avoid – OliverRadini May 15 '19 at 12:58
  • @OliverRadini It does use JQuery, but you can always translate it to plain javascript. – Ryan Wilson May 15 '19 at 13:00
  • @RyanWilson That's kind of the point of this question – OliverRadini May 15 '19 at 13:01

3 Answers3

1

Here is what I get using window.setTimeout :

  var mouseTimer;
  var myVar;
  function mouseDown() { 
      mouseTimer = window.setTimeout(myFunction,500); //set timeout to fire in 2 seconds when the user presses mouse button down
  }

  function myFunction(){ myVar = true;}
  var div = document.getElementById("testBtn");
  testBtn.addEventListener("mousedown", mouseDown);
  document.body.addEventListener("mouseup", removeTimer);  
  
  function removeTimer(){
     if(myVar) console.log("a");  
     if (mouseTimer) window.clearTimeout(mouseTimer);
     myVar = false;
     
   }
<button id="testBtn">Test</button>
RenaudC5
  • 3,553
  • 1
  • 11
  • 29
1

function handleClickHold(el, timeout, callback) {
  let startTime;
  const mouseDown = () => (startTime = Date.now());
  const mouseUp = (e) => Date.now() - startTime > timeout && callback(Date.now() - startTime);
  el.addEventListener("mousedown", mouseDown);
  el.addEventListener("mouseup", mouseUp);
}

const timeout = 500 // time in ms
const btn = document.querySelector('button');
const callback = (ms) => console.log('Held Button for ' + ms + ' ms')
handleClickHold(btn, timeout, callback);
<button>Click</button>

If you want something more general use, you could use a function like this. Takes an element, a timeout, and a callback, so you can apply it in different situations. You can pass the event to the callback as well.

0

Here's one way of doing it, though it feels rather verbose:

const addHoldListener = (element, f, timeout) => {
  let down = 0;
  let up = 0;

  const upListener = () => {
    up = Date.now();
    const diff = up - down;

    if (diff >= timeout) {
      f();
    }

    element.removeEventListener('mouseup', upListener);
  };
  
  const downListener = () => {
    down = Date.now();
    element.addEventListener('mouseup', upListener);
  };
  
  element.addEventListener('mousedown', downListener);
};

addHoldListener(
  document.querySelector('button'),
  () => console.log('a'),
  500
)
<button>Test</button>
OliverRadini
  • 6,238
  • 1
  • 21
  • 46