Cross-browser DOM utilities for event handling, element queries, pointer interactions, and window/document management. These utilities provide a unified API for mouse, touch, and pointer events while handling browser compatibility and providing cleanup mechanisms.
Utilities for adding and managing DOM event listeners with automatic cleanup.
/**
* Adds a DOM event listener and returns a cleanup function
* @param target - Element or EventTarget to attach listener to
* @param eventName - Name of the event to listen for
* @param handler - Event handler function
* @param options - Optional event listener options
* @returns Cleanup function to remove the event listener
*/
function addDomEvent(
target: EventTarget,
eventName: string,
handler: EventListener,
options?: AddEventListenerOptions
): () => void;
/**
* Adds pointer event with mouse button filtering for pointerdown events
* @param target - Element or EventTarget to attach listener to
* @param type - Pointer event type
* @param cb - Mixed event listener that receives both event and point info
* @param options - Optional event listener options
* @returns Cleanup function to remove the event listener
*/
function addPointerEvent(
target: EventTarget,
type: string,
cb: MixedEventListener,
options?: AddEventListenerOptions
): () => void;
interface MixedEventListener {
(e: AnyPointerEvent, info: PointerEventInfo): void;
}
interface PointerEventInfo {
point: Point;
}
type AnyPointerEvent = MouseEvent | TouchEvent | PointerEvent;Usage Examples:
import { addDomEvent, addPointerEvent } from "@chakra-ui/utils";
// Basic event listener with cleanup
function useClickOutside(ref: React.RefObject<HTMLElement>, callback: () => void) {
React.useEffect(() => {
const cleanup = addDomEvent(document, "click", (event) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
callback();
}
});
return cleanup; // Automatic cleanup on unmount
}, [ref, callback]);
}
// Pointer event handling
function useDrag(elementRef: React.RefObject<HTMLElement>) {
React.useEffect(() => {
if (!elementRef.current) return;
const cleanupPointerDown = addPointerEvent(
elementRef.current,
"pointerdown",
(event, info) => {
console.log("Drag started at:", info.point);
// Start drag logic
}
);
const cleanupPointerMove = addPointerEvent(
document,
"pointermove",
(event, info) => {
console.log("Dragging to:", info.point);
// Update drag position
}
);
return () => {
cleanupPointerDown();
cleanupPointerMove();
};
}, []);
}Utilities for extracting coordinate information from various event types.
/**
* Gets coordinate point from mouse, touch, or pointer events
* @param event - Pointer event (mouse, touch, or pointer)
* @param type - Coordinate type ("page" or "client")
* @returns Point object with x and y coordinates
*/
function getEventPoint(event: AnyPointerEvent, type?: PointType): Point;
interface Point {
x: number;
y: number;
}
type PointType = "page" | "client";Usage Examples:
import { getEventPoint } from "@chakra-ui/utils";
function handleInteraction(event: MouseEvent | TouchEvent) {
// Get page coordinates (relative to document)
const pagePoint = getEventPoint(event, "page");
console.log("Page coordinates:", pagePoint.x, pagePoint.y);
// Get client coordinates (relative to viewport)
const clientPoint = getEventPoint(event, "client");
console.log("Viewport coordinates:", clientPoint.x, clientPoint.y);
}
// React component example
function DraggableBox() {
const [position, setPosition] = React.useState({ x: 0, y: 0 });
const handleMouseMove = (event: MouseEvent) => {
const point = getEventPoint(event, "client");
setPosition(point);
};
return (
<div
onMouseMove={handleMouseMove}
style={{ transform: `translate(${position.x}px, ${position.y}px)` }}
>
Draggable content
</div>
);
}Utilities for detecting and handling different event types.
/**
* Type guard that checks if an event is a MouseEvent
* @param event - Event to check
* @returns true if event is a MouseEvent
*/
function isMouseEvent(event: any): event is MouseEvent;
/**
* Type guard that checks if an event is a TouchEvent
* @param event - Pointer event to check
* @returns true if event is a TouchEvent
*/
function isTouchEvent(event: AnyPointerEvent): event is TouchEvent;
/**
* Checks if a touch event has multiple touch points
* @param event - Pointer event to check
* @returns true if event is a multi-touch event
*/
function isMultiTouchEvent(event: AnyPointerEvent): boolean;Usage Examples:
import { isMouseEvent, isTouchEvent, isMultiTouchEvent } from "@chakra-ui/utils";
function handlePointerEvent(event: AnyPointerEvent) {
if (isMouseEvent(event)) {
// Handle mouse-specific logic
console.log("Mouse button:", event.button);
console.log("Ctrl key:", event.ctrlKey);
} else if (isTouchEvent(event)) {
if (isMultiTouchEvent(event)) {
// Handle multi-touch gestures
console.log("Touch count:", event.touches.length);
} else {
// Handle single touch
console.log("Single touch at:", event.touches[0].clientX, event.touches[0].clientY);
}
}
}Utilities for checking element relationships.
/**
* Checks if a parent element contains a child element
* @param parent - Parent element (can be null)
* @param child - Child element to check
* @returns true if parent contains child
*/
function contains(parent: HTMLElement | null, child: HTMLElement): boolean;Usage Examples:
import { contains } from "@chakra-ui/utils";
function useOutsideClick(ref: React.RefObject<HTMLElement>, callback: () => void) {
React.useEffect(() => {
function handleClick(event: MouseEvent) {
const target = event.target as HTMLElement;
if (ref.current && !contains(ref.current, target)) {
callback();
}
}
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick);
}, [ref, callback]);
}
// Modal component with outside click detection
function Modal({ isOpen, onClose, children }: ModalProps) {
const modalRef = React.useRef<HTMLDivElement>(null);
useOutsideClick(modalRef, onClose);
if (!isOpen) return null;
return (
<div className="modal-overlay">
<div ref={modalRef} className="modal-content">
{children}
</div>
</div>
);
}Utilities for accessing window and document objects across different contexts.
/**
* Gets the owner window of an element
* @param node - Element to get window for (optional)
* @returns Window object (globalThis if no element provided)
*/
function getOwnerWindow(node?: Element | null): typeof globalThis;
/**
* Gets the owner document of an element
* @param node - Element to get document for (optional)
* @returns Document object
*/
function getOwnerDocument(node?: Element | null): Document;
/**
* Gets the window object from an event
* @param event - Event to extract window from
* @returns Window object
*/
function getEventWindow(event: Event): typeof window;
/**
* Gets the currently active element in a document
* @param node - Optional element to get active element relative to
* @returns Currently focused element
*/
function getActiveElement(node?: HTMLElement): HTMLElement;Usage Examples:
import {
getOwnerWindow,
getOwnerDocument,
getEventWindow,
getActiveElement
} from "@chakra-ui/utils";
// Cross-frame compatible utilities
function scrollToTop(element?: HTMLElement) {
const window = getOwnerWindow(element);
const document = getOwnerDocument(element);
window.scrollTo({
top: 0,
left: 0,
behavior: "smooth"
});
}
// Focus management
function saveFocus() {
const activeElement = getActiveElement();
return () => {
if (activeElement && activeElement.focus) {
activeElement.focus();
}
};
}
// Event window detection
function handleWindowEvent(event: Event) {
const eventWindow = getEventWindow(event);
if (eventWindow !== window) {
console.log("Event from different window/frame");
}
}
// Portal-compatible hook
function usePortalCompatibleEffect(
callback: (window: Window, document: Document) => void,
deps: any[],
portalContainer?: HTMLElement
) {
React.useEffect(() => {
const window = getOwnerWindow(portalContainer);
const document = getOwnerDocument(portalContainer);
callback(window, document);
}, deps);
}Utility for finding scrollable parent elements.
/**
* Finds the nearest scrollable parent element
* @param el - Element to find scroll parent for
* @returns Nearest scrollable parent element
*/
function getScrollParent(el: HTMLElement): HTMLElement;Usage Examples:
import { getScrollParent } from "@chakra-ui/utils";
// Scroll element into view within its scroll container
function scrollIntoView(element: HTMLElement) {
const scrollParent = getScrollParent(element);
const elementRect = element.getBoundingClientRect();
const parentRect = scrollParent.getBoundingClientRect();
if (elementRect.top < parentRect.top) {
// Element is above visible area
scrollParent.scrollTop -= (parentRect.top - elementRect.top);
} else if (elementRect.bottom > parentRect.bottom) {
// Element is below visible area
scrollParent.scrollTop += (elementRect.bottom - parentRect.bottom);
}
}
// Virtual scrolling helper
function useScrollParent(ref: React.RefObject<HTMLElement>) {
const [scrollParent, setScrollParent] = React.useState<HTMLElement | null>(null);
React.useEffect(() => {
if (ref.current) {
setScrollParent(getScrollParent(ref.current));
}
}, [ref]);
return scrollParent;
}