+ price; }, 1200); }); btnStopSSE.addEventListener('click', function () { if (sseTimer) clearInterval(sseTimer); btnStartSSE.disabled = false; btnStopSSE.disabled = true; if (valStatus) valStatus.textContent = 'Closed (CLOSED)'; }); } // ── Widget 2: Protocol Matrix Selector ── const selProto = document.getElementById('sel-protocol-usecase'); const valProtoName = document.getElementById('val-proto-name'); const valProtoAdv = document.getElementById('val-proto-adv'); if (selProto) { selProto.addEventListener('change', function () { const val = selProto.value; if (val === 'ticker') { if (valProtoName) valProtoName.textContent = 'Server-Sent Events (SSE)'; if (valProtoAdv) valProtoAdv.textContent = 'Unidirectional server push with HTTP/2 multiplexing and auto-reconnect.'; } else if (val === 'chat') { if (valProtoName) valProtoName.textContent = 'WebSockets (ws://)'; if (valProtoAdv) valProtoAdv.textContent = 'Full-duplex bi-directional TCP connection ideal for low-latency messaging.'; } else { if (valProtoName) valProtoName.textContent = 'HTTP Short/Long Polling'; if (valProtoAdv) valProtoAdv.textContent = 'Simple REST GET requests suitable for low-frequency data refresh.'; } }); } // ── Widget 3: Native Modal ── const btnOpenModal = document.getElementById('btn-open-modal'); const demoModal = document.getElementById('demo-modal'); const outModalRes = document.getElementById('out-modal-res'); const valModalRetval = document.getElementById('val-modal-retval'); if (btnOpenModal && demoModal) { btnOpenModal.addEventListener('click', function () { if (typeof demoModal.showModal === 'function') { demoModal.showModal(); } else { alert(' element not supported in your browser.'); } }); demoModal.addEventListener('close', function () { if (outModalRes) outModalRes.style.display = 'block'; if (valModalRetval) valModalRetval.textContent = demoModal.returnValue || 'escaped'; }); } // ── Widget 5: SPA History Router ── const btnHome = document.getElementById('btn-nav-home'); const btnProfile = document.getElementById('btn-nav-profile'); const btnSettings = document.getElementById('btn-nav-settings'); const valHash = document.getElementById('val-hist-hash'); const valLen = document.getElementById('val-hist-len'); function updateHistoryDisplay(route) { history.pushState({ view: route }, '', '#' + route); if (valHash) valHash.textContent = '#' + route; if (valLen) valLen.textContent = history.length; } if (btnHome) btnHome.addEventListener('click', function () { updateHistoryDisplay('home'); }); if (btnProfile) btnProfile.addEventListener('click', function () { updateHistoryDisplay('profile'); }); if (btnSettings) btnSettings.addEventListener('click', function () { updateHistoryDisplay('settings'); }); window.addEventListener('popstate', function (e) { if (e.state && e.state.view) { if (valHash) valHash.textContent = '#' + e.state.view; } }); })();
Done with this chapter?
Mark it complete to track your progress and unlock your certificate.
Next Up

Learner Reviews

Write a Review
Share your experience to help other learners.
Your Rating *
+ price;\n }, 1200);\n });\n\n btnStopSSE.addEventListener('click', function () {\n if (sseTimer) clearInterval(sseTimer);\n btnStartSSE.disabled = false;\n btnStopSSE.disabled = true;\n if (valStatus) valStatus.textContent = 'Closed (CLOSED)';\n });\n }\n\n // ── Widget 2: Protocol Matrix Selector ──\n const selProto = document.getElementById('sel-protocol-usecase');\n const valProtoName = document.getElementById('val-proto-name');\n const valProtoAdv = document.getElementById('val-proto-adv');\n\n if (selProto) {\n selProto.addEventListener('change', function () {\n const val = selProto.value;\n if (val === 'ticker') {\n if (valProtoName) valProtoName.textContent = 'Server-Sent Events (SSE)';\n if (valProtoAdv) valProtoAdv.textContent = 'Unidirectional server push with HTTP/2 multiplexing and auto-reconnect.';\n } else if (val === 'chat') {\n if (valProtoName) valProtoName.textContent = 'WebSockets (ws://)';\n if (valProtoAdv) valProtoAdv.textContent = 'Full-duplex bi-directional TCP connection ideal for low-latency messaging.';\n } else {\n if (valProtoName) valProtoName.textContent = 'HTTP Short/Long Polling';\n if (valProtoAdv) valProtoAdv.textContent = 'Simple REST GET requests suitable for low-frequency data refresh.';\n }\n });\n }\n\n // ── Widget 3: Native Modal ──\n const btnOpenModal = document.getElementById('btn-open-modal');\n const demoModal = document.getElementById('demo-modal');\n const outModalRes = document.getElementById('out-modal-res');\n const valModalRetval = document.getElementById('val-modal-retval');\n\n if (btnOpenModal && demoModal) {\n btnOpenModal.addEventListener('click', function () {\n if (typeof demoModal.showModal === 'function') {\n demoModal.showModal();\n } else {\n alert(' element not supported in your browser.');\n }\n });\n\n demoModal.addEventListener('close', function () {\n if (outModalRes) outModalRes.style.display = 'block';\n if (valModalRetval) valModalRetval.textContent = demoModal.returnValue || 'escaped';\n });\n }\n\n // ── Widget 5: SPA History Router ──\n const btnHome = document.getElementById('btn-nav-home');\n const btnProfile = document.getElementById('btn-nav-profile');\n const btnSettings = document.getElementById('btn-nav-settings');\n const valHash = document.getElementById('val-hist-hash');\n const valLen = document.getElementById('val-hist-len');\n\n function updateHistoryDisplay(route) {\n history.pushState({ view: route }, '', '#' + route);\n if (valHash) valHash.textContent = '#' + route;\n if (valLen) valLen.textContent = history.length;\n }\n\n if (btnHome) btnHome.addEventListener('click', function () { updateHistoryDisplay('home'); });\n if (btnProfile) btnProfile.addEventListener('click', function () { updateHistoryDisplay('profile'); });\n if (btnSettings) btnSettings.addEventListener('click', function () { updateHistoryDisplay('settings'); });\n\n window.addEventListener('popstate', function (e) {\n if (e.state && e.state.view) {\n if (valHash) valHash.textContent = '#' + e.state.view;\n }\n });\n })();\n <\/script>","readTime":8,"category":"html"};
Chapter 1 of ?
html 8 min read

HTML Mastery — Chapter 19: Server-Sent Events & Modern APIs

1. Server-Sent Events (SSE) & EventSource

The Server-Sent Events (SSE) API enables web servers to push real-time data updates to the client over a persistent, single HTTP connection. Unlike WebSockets, SSE is unidirectional (server-to-client) and features built-in automatic reconnects.

// Connect to real-time server SSE endpoint const evtSource = new EventSource('/api/live-stream'); // Default message handler (for unnamed events) evtSource.onmessage = function(event) { console.log("Server payload:", event.data); }; // Custom named event listener evtSource.addEventListener('stock-price', function(event) { const data = JSON.parse(event.data); console.log(`Stock: ${data.symbol} = ${data.price}`); }); // Reconnection & Error handling evtSource.onerror = function(err) { console.warn("Connection lost. Reconnecting automatically..."); };
Try It Yourself »
Try It Yourself — Live Simulated SSE Price Ticker

Start the SSE stream listener to receive server-pushed price updates:


2. Real-Time Protocols Comparison (Polling vs WebSockets vs SSE)

Choosing the right real-time communication technique depends on data directionality, firewall friendliness, and network overhead.

Polling vs WebSockets vs Server-Sent Events (SSE) Protocol Comparison
1. Short Polling Repeated HTTP Requests GET /data (Every 2s) ❌ High HTTP Header Overhead ❌ High Server Load 2. WebSockets (ws://) Full-Duplex TCP Channel Client ➔ Server Server ➔ Client ✅ Bi-Directional Chat / Games 3. SSE (EventSource) Unidirectional HTTP Stream Server ➔ Client Push ✅ Native Auto-Reconnect ✅ HTTP/2 Multiplexing
Try It Yourself — Protocol Selector & Use-Case Matrix

Select a real-time scenario to inspect the ideal protocol architecture:

Recommended Protocol:Server-Sent Events (SSE)
Key Advantage:HTTP/2 multiplexing with built-in auto-reconnection

3. Native HTML5 <dialog> Modal Element (.showModal())

The native <dialog> element replaces custom div-overlay modal hacks with native browser accessibility, focus trapping, and pseudo-element backdrop styling.

Method / API Description
dialog.showModal() Opens dialog as a top-layer modal (traps keyboard focus & renders ::backdrop).
dialog.show() Opens dialog as a non-modal overlay (allows interaction with underlying page).
dialog.close(returnValue) Closes dialog and optionally sets the dialog.returnValue string.
<form method="dialog"> Special form method that automatically closes the parent dialog on submission.

Using Native <dialog> Modal

<!-- Native HTML5 Dialog --> <dialog id="my-modal" class="custom-modal"> <form method="dialog"> <h3>Confirm Action</h3> <p>Are you sure you want to delete this file?</p> <button value="cancel" class="btn btn-secondary">Cancel</button> <button value="confirm" class="btn btn-danger">Delete</button> </form> </dialog> <script> const modal = document.getElementById('my-modal'); modal.showModal(); // Opens modal with backdrop modal.addEventListener('close', () => { console.log(`User selected: ${modal.returnValue}`); }); </script>
Try It Yourself »
Try It Yourself — Native `` Modal Launcher

Click to trigger the native browser modal dialog with ::backdrop blur:

Native HTML5 Modal

This dialog runs natively in the browser's Top-Layer rendering stack. Press Esc or click a button to close.


4. Declarative Popover API (popover & popovertarget)

The baseline Popover API provides zero-JavaScript declarative popups, tooltips, and dropdown menus with native light-dismiss (clicking outside closes the popover).

Popover API & Top-Layer Render Stack Architecture
Standard DOM Hierarchy z-index clipping / overflow:hidden parent BROWSER TOP-LAYER [popover] & <dialog> Top Layer Escapes overflow:hidden & z-index limits!

Declarative HTML Popover Syntax

<!-- Invoker Button (No JS required!) --> <button popovertarget="my-menu" popovertargetaction="toggle" class="btn btn-primary"> Toggle Menu ▾ </button> <!-- Popover Content Container --> <div id="my-menu" popover="auto"> <h4>Quick Navigation</h4> <a href="#profile">User Profile</a> <a href="#settings">Settings</a> </div>
Try It Yourself »
Try It Yourself — Declarative Popover API Inspector

Click the invoker button to trigger a zero-JavaScript popover rendered in the Top-Layer:

Notifications Live

Zero-JS popover active! Click anywhere outside to test native light dismissal.


5. HTML5 History API (pushState & replaceState)

The History API powers client-side routing in Single Page Applications (SPAs) by updating the URL in the address bar without triggering full page reloads.

SPA Client-Side Routing with `pushState`

// 1. Push new URL and State object to history stack history.pushState({ page: 'settings', userId: 42 }, 'Settings', '/settings'); // 2. Replace current history state entry history.replaceState({ page: 'settings-v2' }, 'Settings V2', '/settings-v2'); // 3. Listen for browser back / forward button navigation window.addEventListener('popstate', (event) => { if (event.state) { console.log("Navigated to state:", event.state.page); loadPageContent(event.state.page); } });
Try It Yourself »
Try It Yourself — Live SPA History Router & State Simulator

Click routes below to update location hash state without triggering page reloads:

Current Location Hash:#home
History Stack Length:1

Hands-On Challenge: Build a Real-Time Live Notification Center

Build a real-time notification badge system using EventSource streams that pops open a native <dialog> modal when critical alert payloads arrive.

Launch Code Playground & Try »

Chapter 19 Key Takeaways

  • Use EventSource for unidirectional server-to-client HTTP streaming with native auto-reconnect.
  • Use dialog.showModal() to open accessible modals in the browser's Top-Layer with focus trapping.
  • Use declarative popover="auto" and popovertarget for zero-JS popups with automatic light-dismiss.
  • Use history.pushState() and window.onpopstate for Single Page Application client-side routing.
+ price; }, 1200); }); btnStopSSE.addEventListener('click', function () { if (sseTimer) clearInterval(sseTimer); btnStartSSE.disabled = false; btnStopSSE.disabled = true; if (valStatus) valStatus.textContent = 'Closed (CLOSED)'; }); } // ── Widget 2: Protocol Matrix Selector ── const selProto = document.getElementById('sel-protocol-usecase'); const valProtoName = document.getElementById('val-proto-name'); const valProtoAdv = document.getElementById('val-proto-adv'); if (selProto) { selProto.addEventListener('change', function () { const val = selProto.value; if (val === 'ticker') { if (valProtoName) valProtoName.textContent = 'Server-Sent Events (SSE)'; if (valProtoAdv) valProtoAdv.textContent = 'Unidirectional server push with HTTP/2 multiplexing and auto-reconnect.'; } else if (val === 'chat') { if (valProtoName) valProtoName.textContent = 'WebSockets (ws://)'; if (valProtoAdv) valProtoAdv.textContent = 'Full-duplex bi-directional TCP connection ideal for low-latency messaging.'; } else { if (valProtoName) valProtoName.textContent = 'HTTP Short/Long Polling'; if (valProtoAdv) valProtoAdv.textContent = 'Simple REST GET requests suitable for low-frequency data refresh.'; } }); } // ── Widget 3: Native Modal ── const btnOpenModal = document.getElementById('btn-open-modal'); const demoModal = document.getElementById('demo-modal'); const outModalRes = document.getElementById('out-modal-res'); const valModalRetval = document.getElementById('val-modal-retval'); if (btnOpenModal && demoModal) { btnOpenModal.addEventListener('click', function () { if (typeof demoModal.showModal === 'function') { demoModal.showModal(); } else { alert(' element not supported in your browser.'); } }); demoModal.addEventListener('close', function () { if (outModalRes) outModalRes.style.display = 'block'; if (valModalRetval) valModalRetval.textContent = demoModal.returnValue || 'escaped'; }); } // ── Widget 5: SPA History Router ── const btnHome = document.getElementById('btn-nav-home'); const btnProfile = document.getElementById('btn-nav-profile'); const btnSettings = document.getElementById('btn-nav-settings'); const valHash = document.getElementById('val-hist-hash'); const valLen = document.getElementById('val-hist-len'); function updateHistoryDisplay(route) { history.pushState({ view: route }, '', '#' + route); if (valHash) valHash.textContent = '#' + route; if (valLen) valLen.textContent = history.length; } if (btnHome) btnHome.addEventListener('click', function () { updateHistoryDisplay('home'); }); if (btnProfile) btnProfile.addEventListener('click', function () { updateHistoryDisplay('profile'); }); if (btnSettings) btnSettings.addEventListener('click', function () { updateHistoryDisplay('settings'); }); window.addEventListener('popstate', function (e) { if (e.state && e.state.view) { if (valHash) valHash.textContent = '#' + e.state.view; } }); })();
Done with this chapter?
Mark it complete to track your progress and unlock your certificate.
Next Up

Learner Reviews

Write a Review
Share your experience to help other learners.
Your Rating *