@svelte-put/clickoutside
Installation
npm install --save-dev @svelte-put/clickoutside@^3.0.0pnpm add -D @svelte-put/clickoutside@^3.0.0yarn add -D @svelte-put/clickoutside@^3.0.0Quick Start
<script lang="ts">
import { clickoutside } from '@svelte-put/clickoutside';
function doSomething(e: CustomEvent<MouseEvent>) {
console.log(e.target);
}
</script>
<div use:clickoutside on:clickoutside={doSomething} />Advanced Usage & Customization
Feature Demo
<script lang="ts">
import { clickoutside } from '@svelte-put/clickoutside';
import { quintOut } from 'svelte/easing';
import { fly } from 'svelte/transition';
let enabled = true;
let parent: Element;
let click = 0;
let cls = '';
export { cls as class };
function onClickOutside() {
click++;
}
function toggleEnabled() {
enabled = !enabled;
}
</script>
<fieldset
class="border-4 border-error-text bg-error-surface text-error-text p-10 {cls}"
bind:this={parent}
>
<legend class="font-bold text-error-text">Limit</legend>
<div
class="grid border-2 border-success-element bg-success-surface text-success-text p-6"
use:clickoutside={{ enabled, limit: { parent } }}
on:clickoutside={onClickOutside}
>
<p>
<code>use:clickoutside</code> is registered for this <strong>green box</strong>. Try these:
</p>
<ol>
<li>
Click within this <strong>green</strong> zone => <strong>won't </strong> trigger
<code>on:clickoutside</code>
</li>
<li>
Click on the <strong>red</strong> zone => <strong>will</strong> trigger
<code>on:clickoutside</code>
</li>
<li>
Click outside the <strong>red</strong> limit => <strong>won't</strong> trigger
<code>on:clickoutside</code>
</li>
<li>Enable/disable <code>use:clickoutside</code> with button below, then try (2) again</li>
</ol>
</div>
<div class="flex items-center justify-between">
<p class="">
<code>on:clickoutside</code> counter:
{#key click}
<strong in:fly={{ y: 8, duration: 250, easing: quintOut }} class="inline-block"
>{click}</strong
>
{/key}
</p>
<button class="bg-blue-500 px-2 py-1 text-white" on:click|stopPropagation={toggleEnabled}>
{#if enabled}
Disable
{:else}
Enable
{/if}
</button>
</div>
</fieldset>Limit the Clickoutside Zone
As seen in demo above, the limit.parent option can be set to limit the zone that will trigger clickoutside CustomEvent. By default, there is no limit, and the event listener is attached to document.
<script lang="ts">
let parentNode: HTMLElement;
</script>
<div bind:this={parentNode}>
<div use:clickoutside={{ limit: { parent: parentNode } }}>...</div>
</div>Event Type Customization
By default, clickoutside is based on the click event. You can customize this with the event option.
<div use:clickoutside={{ event: 'mousedown' }}>...</div>AddEventListenerOptions
Additional options can be passed to addEventListener should it be necessary.
<div use:clickoutside={{ options: { passive: true } }}>...</div>
<!-- options => useCapture -->
<div use:clickoutside={{ options: true }}>...</div>Excluding Other Events in the Clickoutside Zone
In the Advanced Usage & Customization section, notice the stopPropagation modifier was added to the click event. Without this, the button would also trigger an clickoutside CustomEvent.
<button on:click|stopPropagation>...</button>This makes sense since the button is technically outside of the element clickoutside is placed on, being within the “clickoutside zone”.
Be aware to reflect customization made to the event listener as mentioned in the last two sections. For example, if the mousedown event is used instead of click, add stopPropagation to mousedown on the element that should not trigger clickoutside.
<div use:clickoutside={{ event: 'mousedown' }}>...</div>
<!-- later -->
<button on:mousedown|stopPropagation>...</button>Below is another demo where stopPropagation is generally recommended. Consider two buttons, each should trigger the corresponding box on its side:
- The green toggle button behaves as expected. Notice the added
|stopPropagationon this button. - The red toggle button does not behave as expected. Specifically:
- when the left green box is toggled on, clicking on this red button causes the green box to toggled off, because the red button is indeed in outside the green box,
- clicking on the red button does not seem to toggle the red box on, for the same reason as before: the box does indeed get toggled on, but immediately off again because the red button triggers both the toggling and the clickoutside events.
<script>
import { clickoutside } from '@svelte-put/clickoutside';
let leftOpen = true;
function toggleLeft() {
leftOpen = !leftOpen;
}
let rightOpen = false;
function toggleRight() {
rightOpen = !rightOpen;
}
let containerEl;
</script>
<div class="relative w-full overflow-hidden" bind:this={containerEl}>
<div
class="absolute inset-y-0 left-0 w-1/3 bg-success-surface text-success-text origin-left grid place-items-center transition-[opacity,transform] {leftOpen ? 'opacity-100 scale-x-100' : 'opacity-50 scale-x-50'}"
use:clickoutside={{ enabled: leftOpen, limit: { parent: containerEl } }}
on:clickoutside={toggleLeft}
>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-move-right"><path d="M18 8L22 12L18 16"/><path d="M2 12H22"/></svg>
</div>
<div class="flex w-1/3 mx-auto">
<button on:click|stopPropagation={toggleLeft} class="inline bg-success-surface-200 text-success-text p-2 active:scale-95 flex-1">
Toggle Left
</button>
<button on:click={toggleRight} class="inline bg-error-surface-200 text-error-text p-2 active:scale-95 flex-1">
Toggle Right
</button>
</div>
<div
class="absolute inset-y-0 right-0 w-1/3 bg-error-surface text-error-text origin-right grid place-items-center {rightOpen ? 'opacity-100 scale-x-100' : 'opacity-50 scale-x-50'}"
use:clickoutside={{ enabled: rightOpen, limit: { parent: containerEl } }}
on:clickoutside={toggleRight}
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-move-left"><path d="M6 8L2 12L6 16"/><path d="M2 12H22"/></svg>
</div>
</div>Happy clicking outside! 👨💻