webdevpuneet.com

Hold to Confirm Button — Free Press and Hold JS Snippet

Hold to Confirm Button · Buttons · Plain HTML, CSS & JS · Live preview

CategoryButtons

Copy & paste

Source Code

<div class="hc-wrap">
  <button type="button" class="hc-btn" id="hcBtn">
    <span class="hc-fill" id="hcFill"></span>
    <span class="hc-label" id="hcLabel">Hold to delete</span>
  </button>
  <p class="hc-status" id="hcStatus">Press and hold for 1.2s</p>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Time-accurate fill
rAF against performance.now() reflects real hold duration.
Pointer capture
Release is caught even if the finger drifts off the button.
Cancel on release
Early release retracts the bar with a brief transition.
Fires exactly once
Completion stops the loop so the action can't double-run.
Clear feedback
A status line states the requirement and the result.
Tunable duration
One HOLD_MS constant sets how long the hold takes.
Touch-friendly
touch-action:none keeps the page still while holding.
No dependency
Pure HTML/CSS/JS — no modal or confirm library.

About this UI Snippet

Hold to Confirm Button — Press-and-Hold Action

Screenshot of the Hold to Confirm Button snippet rendered live

A hold-to-confirm button replaces a two-step "Are you sure?" dialog with a single deliberate gesture: the user presses and holds, a bar fills, and the action only fires when it completes. It's the safety pattern behind "hold to delete", "hold to power off", and destructive actions in apps that want intent without a modal interrupt. This snippet builds it in plain HTML, CSS, and vanilla JavaScript with no dependency.

Time-based progress, not a CSS transition

The fill is driven by requestAnimationFrame against performance.now(), not a fixed CSS animation. Each frame computes progress = elapsed / HOLD_MS and sets the fill width, so the bar's position always reflects exactly how long the button has been held — and releasing mid-way leaves it at the real point reached. Reaching progress >= 1 calls confirm() once and stops the loop, so the action can't double-fire.

Pointer Events with capture

A single set of Pointer Events covers mouse, touch, and pen. On pointerdown the button calls setPointerCapture, which means it keeps receiving pointerup even if the finger drifts off the button — critical on touch, where a small slide would otherwise drop the release event and leave the hold stuck. touch-action:none stops the page scrolling while you hold.

Cancel on early release

pointerup, pointercancel, and a guarded pointerleave all route to cancel(), which stops the animation frame and animates the fill back to zero with a brief CSS transition (re-enabled only for the retract so the fill-up itself stays frame-accurate). A status line gives feedback — "Released too soon" — so the gesture's requirement is obvious without trial and error.

Locked completion and reset

On success the button turns green, swaps its label to "Deleted", and sets done, which makes the next press reset first rather than re-running. Separating confirm(), cancel(), and reset() keeps each transition explicit and makes the control easy to reason about.

Wiring to a real action

Put your destructive call inside confirm() — delete the record, sign out, wipe the cache. Because completion is the single choke point, you get the safety of a held gesture with one line of integration, and you can tune HOLD_MS to match how dangerous the action is.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA red "Hold to delete" button renders with a status line beneath it.
  2. 2
    Press and holdA fill bar sweeps across the button while you keep pressing.
  3. 3
    Release earlyLetting go before it fills retracts the bar and shows a retry hint.
  4. 4
    Hold to completionAt full the button turns green and the action confirms once.
  5. 5
    Press again to resetA completed button resets on the next press before re-running.
  6. 6
    Wire your actionPut the real delete or sign-out call inside confirm().

Real-world uses

Common Use Cases

Destructive actions
Replace a plain confirm dialog with a single deliberate gesture, firing only when a filling bar completes.
Slide-style alternatives
Offer a hold gesture instead of a slide to confirm, measuring real hold time with performance.now() inside requestAnimationFrame.
Account and security actions
Confirm sign-out or data wipe, pairing with a snackbar undo for a second chance after the action has fired.
Bulk operations
Guard a delete inside a bulk actions bar with a hold, using pointer capture so release is detected even if the finger drifts.
Kiosk and touch interfaces
Prevent accidental taps where a loading button would not be enough, with completion stopping the loop so the action never double-fires.
Related: Print This Page Button
See the Print This Page Button for a related buttons pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Because the progress must reflect the real hold duration so an early release leaves the bar exactly where it stopped. Each frame computes progress as elapsed / HOLD_MS from performance.now() and sets the width. A fixed CSS animation couldn't be paused at an arbitrary point or report how far it got when you let go.

It uses Pointer Events and calls setPointerCapture on pointerdown, so the button keeps receiving the pointerup even if your finger slides slightly off it — a common cause of stuck holds otherwise. touch-action:none also stops the page from scrolling while you press.

pointerup, pointercancel, and pointerleave all call cancel(), which stops the animation frame and animates the fill back to zero with a short transition, then shows a "Released too soon" message. The transition is enabled only for the retract so the fill-up itself stays frame-accurate.

No. When progress reaches 1, confirm() runs once and the animation loop stops. A done flag marks the button complete so the next press resets it rather than re-triggering, ensuring the destructive action executes a single time per hold.

Keep the progress and done state in the component and store the rAF id and start time in refs (not state) so updating them doesn't re-render. Attach the pointer handlers to a ref'd button, and call your action inside confirm(). Clean up the animation frame on unmount. In Tailwind, the fill is an absolutely-positioned span with an inline width bound to progress.

Design your website