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.
🚀 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
ES6 Modules (Recommended)
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
- Code Style: Follow the existing TypeScript/ESLint configuration
- Testing: Add tests for new features
- Documentation: Update README and JSDoc comments
- 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]