civil-and-structural-engineering
Using Javascript to Detect and Handle Network Connectivity Changes
Table of Contents
Understanding Network Connectivity Detection in JavaScript
In modern web applications, network connectivity can change frequently—users move between Wi-Fi networks, enter tunnels, or switch to mobile data. A resilient application must gracefully handle these transitions, preserving user experience and data integrity. JavaScript provides a robust set of tools for detecting connectivity changes, but many developers only scratch the surface with a simple navigator.onLine check. This article explores the full spectrum of detecting and handling network connectivity changes, from basic event listeners to advanced techniques like service worker background sync and synthetic connectivity checks.
Core Browser APIs for Connectivity Detection
The navigator.onLine Property
The navigator.onLine property is the most basic and widely supported API for determining whether the browser is currently online. It returns a boolean value: true if the browser believes it has network access, false otherwise. However, this property has notable limitations. It reflects the browser's own internal assessment, which may not accurately represent actual internet reachability. For example, navigator.onLine often returns true even when the user has a local network connection but no internet access. It is best used as a quick initial check rather than a definitive indicator.
To read the current state:
const isOnline = navigator.onLine;
console.log(`Current status: ${isOnline ? 'online' : 'offline'}`);
The online and offline Events
The online and offline events fire on the window object whenever the browser changes its connectivity assessment. These events are the primary mechanism for reacting to network changes in real time. Unlike polling the navigator.onLine property, events allow for immediate updates without wasting CPU cycles.
Basic event listeners:
window.addEventListener('online', () => {
console.log('✅ Browser is online');
updateUI(true);
});
window.addEventListener('offline', () => {
console.log('❌ Browser is offline');
updateUI(false);
});
These events fire on window only, not on elements like document or navigator. They are supported in all modern browsers, including legacy Edge and recent versions of Safari. One key nuance: the events are not guaranteed to fire in all scenarios. For instance, if a laptop wakes from sleep while still disconnected, the browser may not fire the offline event until a network request fails. Developers should treat these events as hints, not guarantees.
Practical Implementation: Building a Connectivity-Aware UI
A common requirement is to display a persistent banner or toast when the user goes offline and hide it when connectivity returns. Below is a production-ready approach that uses both the initial navigator.onLine check and event listeners to ensure the UI is always in sync.
Creating a Network Status Indicator
const statusEl = document.getElementById('network-status');
const offlineBanner = document.getElementById('offline-banner');
function updateNetworkUI(isOnline) {
if (isOnline) {
statusEl.textContent = 'Connected to the internet';
statusEl.className = 'status status--online';
offlineBanner.style.display = 'none';
} else {
statusEl.textContent = 'No internet connection';
statusEl.className = 'status status--offline';
offlineBanner.style.display = 'block';
}
}
// Initial check
updateNetworkUI(navigator.onLine);
// Listen for changes
window.addEventListener('online', () => updateNetworkUI(true));
window.addEventListener('offline', () => updateNetworkUI(false));
Add the corresponding HTML:
<div id="offline-banner" style="display:none; background:#ffe0e0; padding:1rem;">You are currently offline. Changes will be saved locally and synced when you reconnect.</div>
<p id="network-status">Checking connection...</p>
This pattern ensures the user sees immediate feedback. The hidden banner appears only when offline, reducing visual clutter.
Going Beyond the Basic API: Synthetic Connectivity Checks
Because navigator.onLine can be inaccurate, many production applications implement a synthetic connectivity check—a lightweight HTTP or ping request to a reliable server. If the request fails within a short timeout, the application considers the user offline. This approach catches scenarios like DNS failures, proxy issues, or captive portals that the browser API might miss.
Implementing a Network Health Check
A simple implementation uses the fetch API to request a small resource (e.g., a favicon or a minimal endpoint) from your own server or a known CDN. Timeout handling is essential to prevent hanging requests.
async function checkConnectivity(url = 'https://example.com/ping.png', timeout = 3000) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
try {
await fetch(url, { mode: 'no-cors', signal: controller.signal });
clearTimeout(id);
return true;
} catch (err) {
clearTimeout(id);
return false;
}
}
You can then call this function periodically or in response to user actions:
// Example usage in a form submission handler
async function handleSubmit() {
const isOnline = await checkConnectivity();
if (!isOnline) {
alert('No internet connection. Please try again later.');
return;
}
// proceed with submission
}
Be cautious with synthetic checks: too many or too frequent requests can waste bandwidth and battery. A balanced approach is to run the check when the browser reports an offline event (to confirm) or before critical operations like form submission or API calls. Some libraries, like @layer0/offline or online-status, offer more sophisticated polling strategies, but a hand-rolled check gives you full control.
Handling Real-World Scenarios: Queuing, Caching, and Sync
Detecting connectivity is only half the solution. The other half is handling what happens when the user goes offline. Users expect to continue working even without a connection—filling forms, viewing previously loaded data, and saving changes. This is where service workers and IndexedDB come into play.
Queueing User Actions with Service Workers
The Service Worker API enables you to intercept network requests and serve cached responses. For offline-first applications, you can cache key resources (HTML, CSS, JS, images) during the install event. For dynamic data, the Background Sync API allows queuing failed requests and replaying them once the user reconnects:
// In service worker script
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-messages') {
event.waitUntil(syncMessages());
}
});
async function syncMessages() {
const messages = await getMessagesFromIndexedDB();
for (const msg of messages) {
try {
await fetch('/api/messages', { method: 'POST', body: JSON.stringify(msg) });
await removeMessageFromIndexedDB(msg.id);
} catch (err) {
// Retry later via next sync
console.warn('Sync failed, will retry', err);
}
}
}
In the main thread, register a sync when the user goes offline:
if ('serviceWorker' in navigator && 'SyncManager' in window) {
navigator.serviceWorker.ready.then(reg => {
reg.sync.register('sync-messages');
});
}
Background Sync works well in Chrome and Edge; Safari and Firefox have limited or no support. For broader compatibility, you can fall back to periodic navigator.onLine checks with custom retry logic.
Using IndexedDB for Offline Storage
IndexedDB is the primary client-side database for storing structured data. Combined with connectivity detection, you can store user actions (form submissions, edits) locally while offline and sync them when back online:
if (!navigator.onLine) {
const db = await openDB('offline-store', 1);
await db.put('pending-actions', { action: 'update-profile', data: formData, timestamp: Date.now() });
showOfflineSnackbar('Changes saved locally');
} else {
// Send directly to server
await fetch('/api/profile', { method: 'PUT', body: formData });
}
When the online event fires, iterate over pending actions and replay them in order. Ensure idempotency by including unique identifiers and checking for duplicates on the server.
User Feedback and Accessibility
Network connectivity changes affect every user, but not everyone perceives visual cues the same way. When designing offline/online indicators:
- Use multiple sensory channels: In addition to a visual banner, provide a brief text change in a live region (
aria-live="polite") so screen readers announce the status. - Non‑intrusive notifications: Avoid blocking modals during offline events. A persistent banner at the top or bottom of the viewport works well.
- Graceful degradation: Disable buttons that require a network (e.g., “Submit”, “Save to Cloud”) and show a tooltip explaining why.
<div id="offline-banner" role="alert" aria-live="assertive" style="display:none;">You are offline. Your changes will be saved locally.</div>
Cross-Browser and Device Considerations
The navigator.onLine property and related events are supported across all modern browsers, but their behavior can vary:
- Google Chrome: Fires
offlinequickly when network interface drops. Synthetic checks are still recommended for captive portals. - Mozilla Firefox: Similar to Chrome, but may delay offline event in some edge cases.
- Apple Safari: Historically less reliable;
navigator.onLinecan remaintrueeven after losing internet access. Synthetic checks are particularly important. - Microsoft Edge (Chromium-based): Behaves like Chrome. Legacy Edge (EdgeHTML) had good support.
- Mobile browsers: On iOS Safari, the offline event may not fire when switching between Wi‑Fi and cellular if the device maintains a constant connection. Always combine with periodic checks.
For the broadest compatibility, never rely solely on the browser events. A fallback timer—e.g., a setInterval every 30 seconds that performs a lightweight fetch—can catch cases missed by events. However, balance frequency against performance and battery life.
Best Practices for Production Applications
- Layered detection: Use
navigator.onLinefor instant feedback, but validate with synthetic checks before critical operations. - Debounce rapid changes: If a user’s connection is flapping (on‑off‑on within a second), debounce the UI updates to avoid flickering. Use a short delay (e.g., 500ms) before showing an offline banner.
- Cache intelligently: Pre‑cache essential assets and data while online so offline functionality is rich. Use the “offline-first” approach for Progressive Web Apps.
- Communicate clearly: Tell the user what they can still do offline (view, edit drafts) and what requires a connection (search, submit). Avoid generic “Network Error” messages.
- Monitor with analytics: Track offline/online state changes to understand your users’ network environments. This can inform caching strategies and feature priorities.
- Test thoroughly: Simulate offline conditions using browser DevTools (Network tab > “Offline” or “Slow 3G”) and real-world scenarios (airplane mode, VPN disconnects).
Advanced Pattern: Combining Fetch with a Network Status Service
For complex applications (e.g., real‑time collaboration tools, financial dashboards), consider building a dedicated NetworkStatusService class that centralizes all connectivity logic. Such a service can expose a reactive stream (using RxJS or a custom event emitter) that multiple components subscribe to. It can also hold a retry queue and expose methods like waitForOnline():
class NetworkStatusService {
constructor() {
this._listeners = [];
this._online = navigator.onLine;
window.addEventListener('online', () => this._setOnline(true));
window.addEventListener('offline', () => this._setOnline(false));
this._startPeriodicCheck();
}
_setOnline(online) {
if (this._online !== online) {
this._online = online;
this._listeners.forEach(fn => fn(online));
}
}
subscribe(fn) { this._listeners.push(fn); }
isOnline() { return this._online; }
async waitForOnline(timeout = 30000) {
return new Promise((resolve, reject) => {
if (this._online) return resolve();
const timer = setTimeout(() => reject(new Error('Timeout')), timeout);
const unsub = this.subscribe(online => {
if (online) { clearTimeout(timer); unsub(); resolve(); }
});
});
}
_startPeriodicCheck() {
setInterval(async () => {
const result = await checkConnectivity();
this._setOnline(result);
}, 15000); // every 15 seconds
}
}
Usage:
const network = new NetworkStatusService();
network.subscribe(isOnline => {
document.getElementById('banner').hidden = isOnline;
});
// Before critical write
await network.waitForOnline();
// proceed
External Resources and Further Reading
- MDN: Navigator.onLine – Official documentation for the property and events.
- Using Service Workers – MDN guide to implementing offline experiences.
- Network Connections and Offline Detection – Google web.dev resource with practical examples.
- The Offline Cookbook – Jake Archibald’s classic resource on caching and offline strategies.
- Network Connectivity Detection in JavaScript – Smashing Magazine article covering advanced techniques and gotchas.
Conclusion
Detecting and handling network connectivity changes in JavaScript is not a one‑size‑fits‑all task. The combination of the navigator.onLine property, the online/offline events, and synthetic health checks provides a reliable foundation. By layering these techniques with service worker caching, IndexedDB queuing, and thoughtful UI feedback, you can create applications that gracefully degrade and recover, keeping users productive regardless of connection quality. Always test with real‑world network conditions, and remember that the best offline experience is one the user barely notices—until they need it.