JSPM

detect-tab

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 10
  • Score
    100M100P100Q36792F
  • License MIT

A comprehensive tab detection and management library for web applications

Package Exports

  • detect-tab
  • detect-tab/dist/index.esm.js
  • detect-tab/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (detect-tab) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

DetectTab 🔍

A comprehensive tab detection and management library for web applications with TypeScript support.

npm version License: MIT TypeScript

🚀 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

npm install detect-tab
yarn add detect-tab
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

npm install detect-tab

2. Import in Your Project

import { DetectTab, TabState, TabEvent } from 'detect-tab';

CommonJS

const { DetectTab, TabState, TabEvent } = require('detect-tab');

Browser (UMD)

<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:

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:

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:

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:

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:

// 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:

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:

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:

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:

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:

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

// 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

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

const tabDetector = new DetectTab({
    persistent: true,
    storageKey: 'myApp_tabData'
});

// Data is automatically saved to localStorage
// and restored when the page loads

Manual Data Management

// 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

// 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

// 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

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

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

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

git clone https://github.com/yourusername/detect-tab.git
cd detect-tab
npm install

Build

npm run build          # Build all formats
npm run build:types    # Build TypeScript declarations
npm run dev            # Build in watch mode

Testing

npm test              # Run tests
npm run test:watch    # Run tests in watch mode

Linting

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 file for details.

🙋‍♂️ Support

🎯 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]