onOutsideClick
Calls a callback when a click occurs outside the specified element.
1/**
2 * Calls a callback when a click occurs outside the specified element.
3 *
4 * @param element - The target element to detect outside clicks.
5 * @param callback - Function to call when an outside click occurs.
6 * @returns A cleanup function to remove the event listener.
7 */
8export function onOutsideClick(
9 element: HTMLElement,
10 callback: (event: MouseEvent) => void
11): () => void {
12 function handleClick(event: MouseEvent) {
13 if (!element.contains(event.target as Node)) {
14 callback(event);
15 }
16 }
17
18 document.addEventListener('mousedown', handleClick);
19
20 return () => {
21 document.removeEventListener('mousedown', handleClick);
22 };
23}
Encapsulated Event Logic
Abstracts the boilerplate of setting up and tearing down outside click listeners, reducing repetitive code.
Automatic Cleanup Support
Returns a cleanup function that ensures proper unsubscription, preventing memory leaks or event duplication.
Precise Click Detection
Uses
element.contains()
to reliably differentiate between internal and external clicks, even in complex DOM structures.Framework-Agnostic Utility
Works seamlessly in plain JavaScript or within any frontend framework (React, Vue, etc.) without dependencies.
Tests | Examples
1let container: HTMLElement;
2let outside: HTMLElement;
3
4beforeEach(() => {
5 // Create DOM structure
6 container = document.createElement('div');
7 outside = document.createElement('div');
8
9 container.setAttribute('id', 'target');
10 outside.setAttribute('id', 'outside');
11
12 document.body.appendChild(container);
13 document.body.appendChild(outside);
14});
15
16afterEach(() => {
17 document.body.innerHTML = '';
18});
19
20test('calls callback when clicking outside the target element', () => {
21 const callback = jest.fn();
22 const cleanup = onOutsideClick(container, callback);
23
24 // Simulate outside click
25 outside.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
26
27 expect(callback).toHaveBeenCalledTimes(1);
28 cleanup();
29});
30
31test('does not call callback when clicking inside the target element', () => {
32 const callback = jest.fn();
33 const cleanup = onOutsideClick(container, callback);
34
35 // Simulate inside click
36 container.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
37
38 expect(callback).not.toHaveBeenCalled();
39 cleanup();
40});
41
42test('removes event listener on cleanup', () => {
43 const callback = jest.fn();
44 const cleanup = onOutsideClick(container, callback);
45
46 cleanup();
47
48 // Simulate click after cleanup
49 outside.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
50
51 expect(callback).not.toHaveBeenCalled();
52});
Common Use Cases
Closing Modals or Dialogs
Detect user interaction outside a modal to trigger its closure.
Dismissing Dropdowns or Popovers
Hide dropdowns or floating panels when the user clicks elsewhere.
Toggling Sidebar or Menus
Automatically collapse side navigation menus when the main content area is clicked.
Form Interaction Boundaries
Reset form states or validation when clicking outside a specific input group or section.
Click-Outside Confirmation
Handle conditional confirmation (e.g., abandon edits) when clicking outside editable components.