Skip to content

Instantly share code, notes, and snippets.

@james2doyle
Last active December 24, 2025 17:15
Show Gist options
  • Select an option

  • Save james2doyle/e1a03683c730886303917aea26eac22d to your computer and use it in GitHub Desktop.

Select an option

Save james2doyle/e1a03683c730886303917aea26eac22d to your computer and use it in GitHub Desktop.
A few magic properties and directives for Alpine.js
/**
* Alpine.js magic property to confirm a change.
*
* Usage: @click="$confirm('Are you sure?').then(copied = true)"
*
* @returns {(message: string) => Promise<void>} - A function that takes the text to display as an argument.
*/
Alpine.magic('confirm', () => {
return (message) => {
return new Promise((resolve, reject) => {
if (confirm(message)) {
resolve();
} else {
reject();
}
});
};
});
/**
* Alpine.js magic property to clamp a number within a specified range [min, max].
*
* Usage: $clamp(number, min, max)
*
* @returns {(number, min, max) => Number} - A function that returns the transformed number.
*/
Alpine.magic('clamp', () => {
/**
* Clamps a number between a minimum and maximum value.
* Ensures the number does not go below the minimum or exceed the maximum.
*
* @param {number} number The number to clamp.
* @param {number} min The minimum allowed value.
* @param {number} max The maximum allowed value.
* @returns {number} The clamped number.
*/
return (number, min, max) => {
// Ensure min is the lower bound and max is the upper bound, regardless of input order
const lower = Math.min(min, max);
const upper = Math.max(min, max);
// Clamp the number
return Math.min(Math.max(number, lower), upper);
};
});
/**
* Alpine.js magic property to write text to the clipboard.
*
* Usage: $clipboard('text to copy').then(() => { // Success callback });
*
* @returns {(subject: string) => Promise<void>} - A function that takes the text to copy as an argument.
*/
Alpine.magic('clipboard', () => {
return (subject) => navigator.clipboard.writeText(subject);
});
/**
* Alpine.js magic property to get the client rectangle of an element.
*
* Usage: $reportClientRect('#element-id').height;
*
* @returns {(selector: string) => DOMRect | null} - A function that takes an optional selector
* to get the client rectangle of element. If no selector is provided,
* it returns the client rectangle of the element the directive is attached to.
*/
Alpine.magic(
'reportClientRect',
/** @param {HTMLElement} el */ (el) => {
return /** @param {?string} selector */ (selector) =>
(selector
? document.querySelector(selector)
: el
)?.getBoundingClientRect();
},
);
/**
* Alpine.js magic property to scroll an element into view.
* Uses smooth scrolling.
*
* Usage: $scrollIntoView('#element-id')
*
* @returns {(selector: string) => void} A function that accepts a CSS selector and scrolls the first matching element into view.
*/
Alpine.magic('scrollIntoView', () => {
return /** @param {string} selector */ (selector) => {
for (const el of [...document.querySelectorAll(selector)]) {
el.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'center',
});
}
};
});
/**
* Alpine.js directive to run functions based on the scroll direction.
*
* Usage: <div x-scrolled.down="console.log('scrolling down!')" x-scrolled.up="console.log('scrolling up!')"></div>
*
* @param {_} _ - Unused parameter from Alpine.js.
* @param {{modifiers: string[], expression: string, evaluateLater: Function, cleanup: Function}} options - Options provided by Alpine.
* @returns {void}
*/
Alpine.directive(
'scrolled',
(_, { modifiers, expression }, { evaluateLater, cleanup }) => {
// Create a function to evaluate the expression later.
const results = evaluateLater(expression);
// Store the previous scroll position.
let scrollPos = 0;
// The handler function that gets called on scroll.
const handler = () => {
// Determine the scroll direction (up or down).
const direction =
document.body.getBoundingClientRect().top > scrollPos
? 'up'
: 'down';
// If the scroll direction matches a modifier, evaluate the expression.
if (modifiers.includes(direction) && window.scrollY > 0) {
results();
}
// Update the scroll position.
scrollPos = document.body.getBoundingClientRect().top;
};
// Add a scroll event listener to the window.
window.addEventListener('scroll', handler, { passive: true });
// Cleanup function to remove the event listener when the element is removed.
cleanup(() => {
window.removeEventListener('scroll', handler);
});
},
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment