tiny-event-intercept
Version:
Lightweight (~1.0KB) TypeScript library for conditional event interception with browser-standard API. Zero dependencies, SSR compatible, robust edge case handling.
271 lines (207 loc) • 6.83 kB
Markdown
[](https://badge.fury.io/js/tiny-event-intercept)
[](https://www.typescriptlang.org/)
[](https://opensource.org/licenses/MIT)
[](https://bundlephobia.com/package/tiny-event-intercept)
A lightweight (~1.0KB), zero-dependency TypeScript library for conditional event interception with browser-standard API. Perfect for implementing conditional event handling in modern web applications.
**Languages**: English | [简体中文](./README_zh-CN.md)
## Features
- **🔒 Type Safe**: Full TypeScript support with strict typing and IntelliSense
- **🌐 Universal**: SSR compatible, works in Node.js and browser environments
- **🧹 Memory Safe**: Automatic cleanup mechanisms prevent memory leaks
- **⚡ Performance**: Optimized event handling with minimal overhead (~700K ops/sec)
- **📦 Lightweight**: Only ~1.0KB minified, zero dependencies
- **🎯 Browser Standard**: Extends native `AddEventListenerOptions` API
- **🔄 Framework Agnostic**: Works with React, Vue, Svelte, or vanilla JavaScript
## Installation
```bash
npm install tiny-event-intercept
```
## Quick Start
```typescript
import { interceptEvents } from 'tiny-event-intercept'
// Prevent clicks when feature is disabled
let isFeatureEnabled = false
const cleanup = interceptEvents(document, {
events: 'click',
when: () => !isFeatureEnabled, // Only intercept when feature is disabled
listener: (event) => {
console.log('Feature is currently disabled')
event.preventDefault()
event.stopPropagation()
},
})
// Enable feature later
isFeatureEnabled = true
// Clean up when done (removes all event listeners)
cleanup()
```
Creates conditional event interceptors with a browser-standard API.
```typescript
function interceptEvents(target: TargetElement, options: InterceptOptions): CleanupFunction
// Types
type TargetElement = EventTarget | (() => EventTarget | null) | null
type EventTypes = keyof GlobalEventHandlersEventMap | readonly (keyof GlobalEventHandlersEventMap)[]
interface InterceptOptions extends AddEventListenerOptions {
events: EventTypes // Event types to intercept
when: () => boolean // Condition function
listener: EventListener // Event handler
// Inherits: capture?, once?, passive?, signal?
}
type EventTarget = Element | Document | Window
type CleanupFunction = () => void
```
**Parameters:**
- `target` - Target element, function returning element, or null (defaults to document)
- `options` - Intercept options including events, condition, and listener
**Returns:**
- `CleanupFunction` - Function to remove all event listeners
## Real-World Use Cases
### 1. Form Validation Blocking
```typescript
const submitButton = document.querySelector('#submit-btn')
let isFormValid = false
const cleanup = interceptEvents(submitButton, {
events: 'click',
when: () => !isFormValid,
listener: (event) => {
event.preventDefault()
showValidationErrors()
console.log('Form submission blocked - validation failed')
},
})
```
```typescript
let isLoading = false
const cleanup = interceptEvents(document, {
events: ['click', 'keydown', 'submit'],
when: () => isLoading,
listener: (event) => {
event.preventDefault()
event.stopPropagation()
showLoadingMessage('Please wait...')
},
capture: true, // Intercept in capture phase for better control
})
```
```typescript
let isModalOpen = false
const cleanup = interceptEvents(document, {
events: 'keydown',
when: () => isModalOpen,
listener: (event) => {
if (event.key === 'Escape') {
closeModal()
event.preventDefault()
}
},
})
```
```typescript
const featureButton = document.querySelector('#new-feature-btn')
const cleanup = interceptEvents(featureButton, {
events: 'click',
when: () => !window.featureFlags?.newFeatureEnabled,
listener: (event) => {
event.preventDefault()
showFeatureNotAvailable()
},
})
```
```typescript
// Intercept clicks on currently active tab
const cleanup = interceptEvents(() => document.querySelector('.tab.active'), {
events: 'click',
when: () => isTabSwitchingDisabled,
listener: (event) => {
event.preventDefault()
showMessage('Tab switching is temporarily disabled')
},
})
```
```typescript
const controller = new AbortController()
const cleanup = interceptEvents(document.body, {
events: ['mousedown', 'touchstart'],
when: () => isDragModeActive,
listener: (event) => {
startDragOperation(event)
},
capture: true, // Capture phase for early interception
passive: false, // Allow preventDefault()
signal: controller.signal, // AbortController support
})
// Later: abort all listeners
controller.abort()
```
```typescript
import { useEffect, useState } from 'react'
import { interceptEvents } from 'tiny-event-intercept'
function FeatureToggle() {
const [isEnabled, setIsEnabled] = useState(false)
useEffect(() => {
const cleanup = interceptEvents(document, {
events: 'click',
when: () => !isEnabled,
listener: (event) => {
console.log('Feature disabled')
event.preventDefault()
}
})
return cleanup // Cleanup on unmount
}, [isEnabled])
return (
<button onClick={() => setIsEnabled(!isEnabled)}>
{isEnabled ? 'Disable' : 'Enable'} Feature
</button>
)
}
```
```typescript
import { onMounted, onUnmounted, ref } from 'vue'
import { interceptEvents } from 'tiny-event-intercept'
export default {
setup() {
const isEnabled = ref(false)
let cleanup: (() => void) | null = null
onMounted(() => {
cleanup = interceptEvents(document, {
events: 'click',
when: () => !isEnabled.value,
listener: (event) => event.preventDefault(),
})
})
onUnmounted(() => {
cleanup?.()
})
return { isEnabled }
},
}
```
The library provides robust cleanup mechanisms:
- **Manual cleanup**: Call the returned cleanup function
- **Automatic cleanup**: Listeners removed on page unload
- **Idempotent**: Safe to call cleanup multiple times
```typescript
const cleanup = interceptEvents(document, {
events: 'click',
when: () => true,
listener: () => {},
})
// Safe to call multiple times
cleanup()
cleanup() // No errors
```
MIT