Last active
December 24, 2025 17:15
-
-
Save james2doyle/e1a03683c730886303917aea26eac22d to your computer and use it in GitHub Desktop.
A few magic properties and directives for Alpine.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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