Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Reusable Snippets|Practical utility code for everyday use — custom-built and ready to share

onOutsideClick

Calls a callback when a click occurs outside the specified element.

TypeScript
Copied!
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

TypeScript
Copied!
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.

Codebase: Utilities -> Browser & DOM -> onOutsideClick | Yevhen Klymentiev