Svelte Long Press
Asked Answered
D

2

5

I need a long press event to bind to buttons in svelte 3. I want to do this in the least "boilerplaty" way possible.

I've tried with a long press function but this seems a little convoluted and hacky, also seems a little slow.

function longPress(node, callback) {
     console.log(node)
    function onmousedown(event) {
      const timeout = setTimeout(() => callback(node.innerHTML), 1000);

      function cancel() {
        clearTimeout(timeout);
        node.removeEventListener("mouseup", cancel, false);
      }

      node.addEventListener("mouseup", cancel, false);
    }

    node.addEventListener("mousedown", onmousedown, false);

    return {
      destroy() {
        node.removeEventListener("mousedown", onmousedown, false);
      }
    };
  }
</script>

<div>
  <Video />
  {#each Object.entries(bindings) as [id, value]}
    <button on:click = {()=>longPress(this,addImage)}> {id} </button>
  {/each}
</div>

This works but I'm sure there is a better way.

Diastrophism answered 2/7, 2019 at 3:9 Comment(0)
C
13

For this sort of thing I would use an action, which is a function that runs when an element is created (and can return functions that are run when parameters are changed, or the element is destroyed): https://svelte.dev/tutorial/actions

In this case you could create a reusable longpress action, much like your function above, which dispatches a custom longpress event on the target element that you can listen for like a native DOM event:

<script>
  import { longpress } from './actions.js';
  let pressed;
</script>

<button use:longpress on:longpress="{e => pressed = true}">
  longpress me
</button>
export function longpress(node, threshold = 500) {
  // note — a complete answer would also consider touch events

  const handle_mousedown = () => {
    let start = Date.now();

    const timeout = setTimeout(() => {
      node.dispatchEvent(new CustomEvent('longpress'));
    }, threshold);

    const cancel = () => {
      clearTimeout(timeout);
      node.removeEventListener('mousemove', cancel);
      node.removeEventListener('mouseup', cancel);
    };

    node.addEventListener('mousemove', cancel);
    node.addEventListener('mouseup', cancel);
  }

  node.addEventListener('mousedown', handle_mousedown);

  return {
    destroy() {
      node.removeEventListener('mousedown', handle_mousedown);
    }
  };
}

The advantage of this approach is that you've separated the definition of 'longpress' from the thing that handles it, so the addImage/node.innerHTML logic can be cleanly separated, and you can re-use the action elsewhere in your app.

Full demo, including passing a parameter to the action: https://svelte.dev/repl/f34b6159667247e6b6abb5142b276483?version=3.6.3

Chatterer answered 2/7, 2019 at 13:12 Comment(6)
Awesome stuff, just change setTimeout to setInterval.Diastrophism
setTimeout is correct. setInterval would repeat it.Josphinejoss
@sudobangbang Right that was what I needed to do. This makes for an even more re-usable function.Diastrophism
How to make on:click co-exist with on:longpress? Click gets triggered for longpress as wellArriviste
@SaravanabalagiRamachandran to prevent the click from triggering when user actually intends to do a long press, listen for "mouseup" event instead of "click". Mouseup means user releases the mouse/press, and so you can determine whether it was a long press or a short tap.Hampden
@SaravanabalagiRamachandran any updates? I've run into the same issue.Ventricose
B
2

I would recommend using 'press' action in svelte-gestures library if you want consistent support on desktop and mobile devices. It allows setting up duration time.

Bosporus answered 4/9, 2022 at 8:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.