JAVASCRIPT

Build a Vue 3 Composable for Detecting Clicks Outside an Element

Develop a reusable Vue 3 Composition API composable, `useClickOutside`, to detect clicks outside a specified element, useful for dropdowns or modals.

// composables/useClickOutside.js
import { onMounted, onUnmounted } from 'vue';

/**
 * Composable to detect clicks outside a specified element.
 * @param {Ref<HTMLElement>} elementRef - A ref to the target element.
 * @param {Function} callback - The function to execute when a click outside occurs.
 */
export function useClickOutside(elementRef, callback) {
  if (!elementRef || !callback) {
    console.error('useClickOutside requires an elementRef and a callback function.');
    return;
  }

  const handler = (event) => {
    // Check if the clicked target is outside the elementRef's value
    // and ensure elementRef.value exists (element might be unmounted).
    if (elementRef.value && !elementRef.value.contains(event.target)) {
      callback(event);
    }
  };

  onMounted(() => {
    // Add event listener to the document when the component is mounted
    document.addEventListener('click', handler);
  });

  onUnmounted(() => {
    // Remove event listener when the component is unmounted to prevent memory leaks
    document.removeEventListener('click', handler);
  });
}

// Usage in a Vue component (e.g., for a dropdown):
<template>
  <div ref="dropdownContainer" style="position: relative; display: inline-block;">
    <button @click="showDropdown = !showDropdown" class="dropdown-toggle">Toggle Dropdown</button>
    <div v-if="showDropdown" class="dropdown-content">
      <p>Dropdown content here</p>
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    </div>
  </div>
  <p style="margin-top: 20px;">Click outside the dropdown area to hide it.</p>
</template>

<script setup>
import { ref } from 'vue';
import { useClickOutside } from './composables/useClickOutside.js';

const dropdownContainer = ref(null); // Create a ref to attach to the element
const showDropdown = ref(false);

useClickOutside(dropdownContainer, () => {
  if (showDropdown.value) {
    showDropdown.value = false;
    console.log('Clicked outside dropdownContainer, dropdown hidden.');
  }
});
</script>

<style scoped>
.dropdown-toggle {
  padding: 10px 15px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.dropdown-content {
  position: absolute;
  top: 100%; /* Position below the button */
  left: 0;
  border: 1px solid #ccc;
  padding: 10px;
  margin-top: 5px;
  background-color: white;
  box-shadow: 0 2px 5px rgba(0,0,0,0.2);
  min-width: 150px;
  z-index: 10;
  border-radius: 4px;
}

.dropdown-content ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.dropdown-content li {
  padding: 5px 0;
  cursor: pointer;
}

.dropdown-content li:hover {
  background-color: #f0f0f0;
}
</style>
How it works: This snippet provides a `useClickOutside` composable for Vue 3, a highly common and useful pattern for UI components like dropdowns, modals, or context menus. It accepts a `ref` to the target element and a callback function. Using `onMounted` and `onUnmounted`, it efficiently attaches and removes a global `click` event listener to the `document`. The `handler` function checks if the click occurred outside the referenced element, executing the provided callback when an outside click is detected. This ensures components can react to user interaction outside their boundaries while preventing memory leaks.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs