UNPKG

detect-tab

Version:

A comprehensive tab detection and management library for web applications

771 lines (588 loc) 19.8 kB
# DetectTab 🔍 A comprehensive tab detection and management library for web applications with TypeScript support. [![npm version](https://badge.fury.io/js/detect-tab.svg)](https://badge.fury.io/js/detect-tab) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) ## 🚀 Features - **Tab Visibility Detection**: Detect when your tab becomes visible or hidden - **Focus Management**: Track when your tab gains or loses focus - **Time Tracking**: Monitor time spent visible, hidden, and in current state - **Event System**: Comprehensive event listeners for all tab state changes - **Performance Optimization**: Built-in debouncing and efficient event handling - **Persistence**: Optional localStorage integration for session tracking - **TypeScript Support**: Full TypeScript definitions included - **Browser Compatibility**: Works across modern browsers with graceful degradation - **Lightweight**: Minimal bundle size with zero dependencies - **Easy Integration**: Simple API with both instance-based and functional approaches ## 📦 Installation ```bash npm install detect-tab ``` ```bash yarn add detect-tab ``` ```bash pnpm add detect-tab ``` ## 🏃‍♂️ Quick Start This guide provides detailed instructions on how to use the DetectTab library in various scenarios. ## Installation & Setup ### 1. Install the Package ```bash npm install detect-tab ``` ### 2. Import in Your Project #### ES6 Modules (Recommended) ```javascript import { DetectTab, TabState, TabEvent } from 'detect-tab'; ``` #### CommonJS ```javascript const { DetectTab, TabState, TabEvent } = require('detect-tab'); ``` #### Browser (UMD) ```html <script src="node_modules/detect-tab/dist/index.umd.js"></script> <script> const { DetectTab, TabState, TabEvent } = window.DetectTab; </script> ``` ## Basic Usage ### Quick Start - Default Instance For simple usage, you can use the pre-created default instance: ```javascript import { isVisible, isFocused, getState, getTabInfo } from 'detect-tab'; // Check current state console.log('Is visible:', isVisible()); console.log('Is focused:', isFocused()); console.log('Current state:', getState()); // Get detailed information const info = getTabInfo(); console.log('Tab info:', info); ``` ### Creating Custom Instances For more control, create your own instance: ```javascript import { DetectTab } from 'detect-tab'; const tabDetector = new DetectTab({ autoStart: true, // Start detection immediately debounceTime: 100, // Debounce events by 100ms debug: false, // Disable debug logging persistent: false, // Don't persist data storageKey: 'myTab' // Custom storage key }); ``` ## Event Handling ### State Change Events Listen for when the tab becomes visible or hidden: ```javascript tabDetector.onStateChange((state, info) => { switch (state) { case TabState.VISIBLE: console.log('Tab is now visible'); resumeAnimations(); break; case TabState.HIDDEN: console.log('Tab is now hidden'); pauseAnimations(); break; case TabState.PRERENDER: console.log('Tab is being prerendered'); break; } }); ``` ### Focus Change Events Listen for when the tab gains or loses focus: ```javascript tabDetector.onFocusChange((focused, info) => { if (focused) { console.log('Tab gained focus'); clearNotificationBadge(); resumeKeyboardShortcuts(); } else { console.log('Tab lost focus'); pauseKeyboardShortcuts(); } }); ``` ### Specific Event Listeners Listen for specific browser events: ```javascript // Visibility change tabDetector.on(TabEvent.VISIBILITY_CHANGE, (info) => { console.log('Visibility changed:', info.visible); }); // Focus events tabDetector.on(TabEvent.FOCUS, (info) => { console.log('Tab focused'); }); tabDetector.on(TabEvent.BLUR, (info) => { console.log('Tab blurred'); }); // Page lifecycle events tabDetector.on(TabEvent.BEFORE_UNLOAD, (info) => { console.log('Page is about to unload'); saveUserData(info); }); tabDetector.on(TabEvent.PAGE_HIDE, (info) => { console.log('Page is hidden (mobile switch, etc.)'); }); ``` ## Common Use Cases ### 1. Performance Optimization Pause expensive operations when the tab is hidden: ```javascript import { DetectTab, TabState } from 'detect-tab'; const tabDetector = new DetectTab(); let animationFrame; let updateInterval; tabDetector.onStateChange((state) => { if (state === TabState.HIDDEN) { // Pause animations if (animationFrame) { cancelAnimationFrame(animationFrame); } // Reduce update frequency clearInterval(updateInterval); updateInterval = setInterval(updateData, 10000); // 10 seconds // Pause videos document.querySelectorAll('video').forEach(video => { video.pause(); }); } else if (state === TabState.VISIBLE) { // Resume animations startAnimations(); // Normal update frequency clearInterval(updateInterval); updateInterval = setInterval(updateData, 1000); // 1 second // Resume videos document.querySelectorAll('video').forEach(video => { video.play(); }); } }); ``` ### 2. User Experience Enhancement Improve UX with smart notifications and state management: ```javascript import { DetectTab, TabEvent } from 'detect-tab'; const tabDetector = new DetectTab(); let notificationCount = 0; // Clear notifications when user returns tabDetector.onFocusChange((focused) => { if (focused) { // Clear notification badge document.title = document.title.replace(/^\(\d+\) /, ''); notificationCount = 0; // Mark messages as read markMessagesAsRead(); } }); // Show notification when hidden and new message arrives function onNewMessage(message) { if (!tabDetector.isVisible()) { notificationCount++; document.title = `(${notificationCount}) ${originalTitle}`; // Show browser notification if (Notification.permission === 'granted') { new Notification('New message', { body: message.text, icon: '/favicon.ico' }); } } } ``` ### 3. Analytics and Tracking Track user engagement and session data: ```javascript import { DetectTab, TabEvent } from 'detect-tab'; const tabDetector = new DetectTab({ persistent: true, // Save data across sessions debug: false }); // Track session start tabDetector.on(TabEvent.FOCUS, (info) => { analytics.track('session_start', { timestamp: Date.now(), totalVisibleTime: info.totalVisibleTime }); }); // Track engagement tabDetector.on(TabEvent.VISIBILITY_CHANGE, (info) => { analytics.track('engagement_change', { visible: info.visible, timeInState: info.timeInState, visibilityChanges: info.visibilityChanges }); }); // Track session end tabDetector.on(TabEvent.BEFORE_UNLOAD, (info) => { const stats = tabDetector.getTimeStats(); analytics.track('session_end', { totalVisibleTime: info.totalVisibleTime, totalHiddenTime: info.totalHiddenTime, visibilityChanges: info.visibilityChanges, sessionDuration: info.totalVisibleTime + info.totalHiddenTime }); }); ``` ### 4. Real-time Features Management Optimize real-time connections based on tab state: ```javascript import { DetectTab } from 'detect-tab'; const tabDetector = new DetectTab(); let websocket; let updateFrequency = 1000; // 1 second function adjustWebSocketFrequency() { if (websocket) { websocket.send(JSON.stringify({ type: 'set_frequency', frequency: updateFrequency })); } } tabDetector.onStateChange((state, info) => { if (state === 'hidden') { // Reduce frequency when hidden updateFrequency = 30000; // 30 seconds } else if (state === 'visible') { // Normal frequency when visible updateFrequency = info.focused ? 1000 : 5000; // 1s focused, 5s unfocused } adjustWebSocketFrequency(); }); tabDetector.onFocusChange((focused) => { if (tabDetector.isVisible()) { updateFrequency = focused ? 1000 : 5000; adjustWebSocketFrequency(); } }); ``` ### 5. Battery and Performance Optimization Advanced optimizations for mobile and battery life: ```javascript import { DetectTab, TabState } from 'detect-tab'; const tabDetector = new DetectTab(); class PerformanceManager { constructor() { this.setupTabDetection(); this.highPerformanceMode = true; } setupTabDetection() { tabDetector.onStateChange((state) => { switch (state) { case TabState.HIDDEN: this.enableBatterySavingMode(); break; case TabState.VISIBLE: this.disableBatterySavingMode(); break; } }); } enableBatterySavingMode() { this.highPerformanceMode = false; // Reduce animation frame rate this.setAnimationFrameRate(15); // 15 FPS // Pause non-essential background tasks this.pauseBackgroundTasks(); // Reduce network requests this.setNetworkRequestInterval(60000); // 1 minute console.log('Battery saving mode enabled'); } disableBatterySavingMode() { this.highPerformanceMode = true; // Normal animation frame rate this.setAnimationFrameRate(60); // 60 FPS // Resume background tasks this.resumeBackgroundTasks(); // Normal network requests this.setNetworkRequestInterval(5000); // 5 seconds console.log('High performance mode enabled'); } setAnimationFrameRate(fps) { this.targetFrameTime = 1000 / fps; // Implement frame rate limiting logic } pauseBackgroundTasks() { // Pause data processing, analytics, etc. } resumeBackgroundTasks() { // Resume background tasks } setNetworkRequestInterval(interval) { clearInterval(this.networkInterval); this.networkInterval = setInterval(() => { if (tabDetector.isVisible()) { this.fetchUpdates(); } }, interval); } fetchUpdates() { // Fetch data updates } } const perfManager = new PerformanceManager(); ``` ## Time Statistics ### Getting Time Information ```javascript // Get formatted time statistics const stats = tabDetector.getTimeStats(); console.log('Visible time:', stats.totalVisibleTime); console.log('Hidden time:', stats.totalHiddenTime); console.log('Current state time:', stats.timeInCurrentState); // Get raw time data const info = tabDetector.getTabInfo(); console.log('Raw visible time (ms):', info.totalVisibleTime); console.log('Raw hidden time (ms):', info.totalHiddenTime); console.log('Visibility changes:', info.visibilityChanges); ``` ### Custom Time Tracking ```javascript class TimeTracker { constructor() { this.tabDetector = new DetectTab({ persistent: true }); this.setupTracking(); } setupTracking() { // Update display every second setInterval(() => { this.updateTimeDisplay(); }, 1000); // Log milestone times this.tabDetector.onStateChange((state, info) => { const totalTime = info.totalVisibleTime + info.totalHiddenTime; // Log every 5 minutes of total time if (totalTime > 0 && totalTime % 300000 === 0) { console.log(`Milestone: ${Math.floor(totalTime / 60000)} minutes total time`); } }); } updateTimeDisplay() { const stats = this.tabDetector.getTimeStats(); const displayEl = document.getElementById('time-display'); if (displayEl) { displayEl.innerHTML = ` <div>Visible: ${stats.totalVisibleTime}</div> <div>Hidden: ${stats.totalHiddenTime}</div> <div>Current: ${stats.timeInCurrentState}</div> `; } } } ``` ## Persistence and Storage ### Enable Persistence ```javascript const tabDetector = new DetectTab({ persistent: true, storageKey: 'myApp_tabData' }); // Data is automatically saved to localStorage // and restored when the page loads ``` ### Manual Data Management ```javascript // Clear stored data tabDetector.clearPersistedData(); // Reset all statistics tabDetector.reset(); // Get current data for custom storage const info = tabDetector.getTabInfo(); localStorage.setItem('customTabData', JSON.stringify(info)); ``` ## Cleanup and Memory Management ### Proper Cleanup ```javascript // When your app/component is destroyed function cleanup() { tabDetector.destroy(); } // For single-page applications window.addEventListener('beforeunload', cleanup); // For React components useEffect(() => { return () => { tabDetector.destroy(); }; }, []); ``` ### Event Listener Management ```javascript // Add listeners const stateCallback = (state, info) => { /* ... */ }; const focusCallback = (focused, info) => { /* ... */ }; tabDetector.onStateChange(stateCallback); tabDetector.onFocusChange(focusCallback); // Remove specific listeners tabDetector.offStateChange(stateCallback); tabDetector.offFocusChange(focusCallback); // Or destroy the instance to remove all listeners tabDetector.destroy(); ``` ## Error Handling ### Graceful Error Handling ```javascript try { const tabDetector = new DetectTab({ debug: true // Enable debug mode to see errors }); // Add error handling for callbacks tabDetector.onStateChange((state, info) => { try { // Your code here performStateChange(state); } catch (error) { console.error('Error in state change handler:', error); // Fallback behavior } }); } catch (error) { console.error('Failed to initialize DetectTab:', error); // Fallback: use basic visibility detection document.addEventListener('visibilitychange', () => { console.log('Visibility changed (fallback)'); }); } ``` ## Browser Compatibility ### Feature Detection ```javascript import { isVisibilityAPISupported, isBrowserSupported } from 'detect-tab'; if (!isBrowserSupported()) { console.warn('DetectTab requires a browser environment'); // Fallback logic } if (!isVisibilityAPISupported()) { console.warn('Page Visibility API not supported, using focus/blur only'); // Limited functionality warning } ``` ## TypeScript Usage ### Type-Safe Implementation ```typescript import { DetectTab, TabState, TabEvent, TabInfo, DetectTabOptions, TabStateChangeCallback } from 'detect-tab'; class TypedTabManager { private tabDetector: DetectTab; private callbacks: Map<string, Function> = new Map(); constructor(options?: DetectTabOptions) { this.tabDetector = new DetectTab(options); this.setupEventHandlers(); } private setupEventHandlers(): void { const stateCallback: TabStateChangeCallback = (state: TabState, info: TabInfo) => { this.handleStateChange(state, info); }; this.tabDetector.onStateChange(stateCallback); this.callbacks.set('stateChange', stateCallback); } private handleStateChange(state: TabState, info: TabInfo): void { switch (state) { case TabState.VISIBLE: this.onVisible(info); break; case TabState.HIDDEN: this.onHidden(info); break; default: console.warn(`Unhandled state: ${state}`); } } private onVisible(info: TabInfo): void { console.log('Tab visible:', info); } private onHidden(info: TabInfo): void { console.log('Tab hidden:', info); } public getInfo(): TabInfo { return this.tabDetector.getTabInfo(); } public destroy(): void { this.tabDetector.destroy(); this.callbacks.clear(); } } ``` ### Graceful Degradation - If Page Visibility API is not supported, the library falls back to basic focus/blur detection - If localStorage is not available, persistence features are disabled - All features degrade gracefully without breaking functionality ### Supported Browsers - ✅ Chrome 14+ - ✅ Firefox 18+ - ✅ Safari 7+ - ✅ Edge 12+ - ✅ Opera 15+ - ✅ iOS Safari 7+ - ✅ Android Browser 4.4+ ## 🏗️ Development ### Setup ```bash git clone https://github.com/yourusername/detect-tab.git cd detect-tab npm install ``` ### Build ```bash npm run build # Build all formats npm run build:types # Build TypeScript declarations npm run dev # Build in watch mode ``` ### Testing ```bash npm test # Run tests npm run test:watch # Run tests in watch mode ``` ### Linting ```bash npm run lint # Check for linting errors npm run lint:fix # Fix linting errors ``` ## 🤝 Contributing Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change. ### Development Guidelines 1. **Code Style**: Follow the existing TypeScript/ESLint configuration 2. **Testing**: Add tests for new features 3. **Documentation**: Update README and JSDoc comments 4. **Backwards Compatibility**: Maintain API compatibility ### Commit Message Format ``` type(scope): description [optional body] [optional footer] ``` Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore` ## 📄 License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## 🙋‍♂️ Support - 📖 [Documentation](https://github.com/meteor314/detect-tab#readme) - 🐛 [Issue Tracker](https://github.com/meteor314/detect-tab/issues) - 💬 [Discussions](https://github.com/meteor314/detect-tab/discussions) ## 🎯 Roadmap - [ ] React/Vue/Angular integration helpers - [ ] Web Workers support - [ ] Service Worker integration - [ ] Performance metrics dashboard - [ ] Advanced analytics features - [ ] Mobile app integration (React Native/Ionic) ## 📊 Changelog ### v1.0.0 - Initial release - Full TypeScript support - Comprehensive tab detection - Event system - Persistence support - Browser compatibility - Complete test coverage --- Made with ❤️ by [meteor314]