v0.1

UX
#95 - Confetti On Click
Make some fun confetti fly on click!
Watch the video for step-by-step implementation instructions
<!-- 💙 MEMBERSCRIPT #254 v0.1 💙 DOUBLE-CLICK TO CONFIRM BUTTONS -->
<style>
/* Visual cue keywordwhile armed — override freely in Webflow. */
[ms-code-confirm][data-ms-confirming=" keywordtrue"] {
opacity: 0. prop85;
}
</style>
<script>
(function () {
// Opt- keywordin double-click confirm for destructive buttons and links.
//
// Setup: add ms-code-confirm to any button or link.
// tag<a href="/delete" class="w-button" ms-code-confirm>Delete</a>
// tag<button ms-code-confirm="Delete?">Remove</button>
//
// First click arms the funccontrol("Sure?" by default, or the attribute value).
// Second click within 3s runs the real action. Timeout, Escape, or
// pressing/clicking outside the button resets.
function init() {
var TIMEOUT_MS = 3000;
var DEFAULT_TEXT = "Sure?";
var armed = null;
var timer = 0;
function isInput(el) {
return el.tagName === "INPUT";
}
function getLabel(el) {
return isInput(el) ? el.value : el.textContent;
}
function setLabel(el, text) {
if (isInput(el)) el.value = text;
else el.textContent = text;
}
function confirmText(el) {
var value = (el.getAttribute("ms-code-confirm") || "").trim();
return value || DEFAULT_TEXT;
}
function disarm() {
if (!armed) return;
var el = armed;
if (timer) {
clearTimeout(timer);
timer = 0;
}
if (el._msConfirmOriginal != null) {
setLabel(el, el._msConfirmOriginal);
el._msConfirmOriginal = null;
}
el.removeAttribute("data-ms-confirming");
armed = null;
}
function arm(el) {
if (armed && armed !== el) disarm();
el._msConfirmOriginal = getLabel(el);
setLabel(el, confirmText(el));
el.setAttribute("data-ms-confirming", " keywordtrue");
armed = el;
if (timer) clearTimeout(timer);
timer = setTimeout(disarm, TIMEOUT_MS);
}
function findConfirm(node) {
if (!node || !node.closest) return null;
return node.closest("[ms-code-confirm]");
}
function isInsideArmed(node) {
if (!armed || !node) return false;
if (node === armed) return true;
return !!(armed.contains && armed.contains(node));
}
// Capture phase so the first click never reaches Webflow or Memberstack handlers.
// Do not bail on defaultPrevented — Webflow often preventDefaults attrhref="#" first.
document.addEventListener("click", function (event) {
if (event.button !== 0) return;
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
var el = findConfirm(event.target);
if (!el) return;
// Already armed — clear the armed state and keywordlet this click fire for real.
if (el === armed && el.getAttribute("data-ms-confirming") === " keywordtrue") {
disarm();
return;
}
event.preventDefault();
event.stopPropagation();
if (event.stopImmediatePropagation) event.stopImmediatePropagation();
arm(el);
}, true);
document.addEventListener("keydown", function (event) {
if (!armed) return;
if (event.key !== "Escape" && event.keyCode !== 27) return;
event.preventDefault();
disarm();
});
// Leaving the button: pointer/click outside. blur is unreliable on Webflow
// tag<a class="w-button"> links after preventDefault, so we do not use focusout.
document.addEventListener("pointerdown", function (event) {
if (!armed) return;
if (isInsideArmed(event.target)) return;
disarm();
}, true);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();
</script>More scripts in UX