Modern Web Standards Guide. Comprehensive overview of current web technologies including HTML5, CSS3, JavaScript ES6+, HTTP/2, Progressive Web Apps, and essential web APIs. This tutorial provides developers with a solid foundation in modern web standards in approximately 30 minutes of reading.
1. Modern Web Standards Overview
The modern web ecosystem has evolved significantly from static HTML pages to dynamic, interactive applications. Understanding current web standards is crucial for developing robust, accessible, and performant web applications.
1.1. Web Architecture Today
Modern web applications follow a client-server architecture with enhanced capabilities:
-
Web clients (browsers) support advanced JavaScript engines, native APIs, and Progressive Web App features
-
Web servers handle HTTP/2, WebSocket connections, and API endpoints
-
Content Delivery Networks (CDNs) optimize global content delivery
-
Progressive Web Apps blur the line between web and native applications
1.2. Core Web Standards
The web platform is built on several foundational standards:
- HTML5
-
The latest markup language standard providing semantic elements, media support, and form enhancements
- CSS3
-
Advanced styling capabilities including Grid, Flexbox, animations, and responsive design
- JavaScript ES6+
-
Modern JavaScript with modules, async/await, classes, and comprehensive APIs
- HTTP/2
-
Improved protocol with multiplexing, server push, and enhanced performance
- Web APIs
-
Browser-native APIs for device access, storage, and advanced functionality
1.3. Modern Web Development Principles
Contemporary web development follows these key principles:
- Performance First
-
Optimize loading times, runtime performance, and resource usage
- Mobile First
-
Design for mobile devices and progressively enhance for larger screens
- Accessibility
-
Ensure applications are usable by everyone, including users with disabilities
- Progressive Enhancement
-
Build basic functionality first, then add advanced features
- Security by Default
-
Implement HTTPS, Content Security Policy, and secure coding practices
1.4. Browser Capabilities
Modern browsers provide extensive capabilities:
-
JavaScript Engines: V8 (Chrome/Edge), SpiderMonkey (Firefox), JavaScriptCore (Safari)
-
Rendering Engines: Blink (Chrome/Edge), Gecko (Firefox), WebKit (Safari)
-
Developer Tools: Built-in debugging, performance profiling, and testing tools
-
Standards Compliance: Regular updates supporting latest web standards
1.5. Development Ecosystem
The web development ecosystem includes:
-
Build Tools: Webpack, Vite, Parcel for bundling and optimization
-
Frameworks: React, Vue, Angular for application structure
-
Package Managers: npm, yarn for dependency management
-
Testing Tools: Jest, Cypress, Playwright for automated testing
2. HTML5 - Modern Markup Language
HTML5 is the current standard for structuring web content. It provides semantic elements, multimedia support, form enhancements, and APIs for interactive applications.
2.1. Semantic HTML Elements
HTML5 introduces semantic elements that provide meaning to content structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Semantic HTML5 Example</title>
</head>
<body>
<header>
<h1>My Blog</h1>
<nav>
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</header>
<main>
<article>
<header>
<h2>Article Title</h2>
<time datetime="2024-12-28">December 28, 2024</time>
</header>
<p>This is the main content of the article...</p>
<footer>
<p>Tags: <span>HTML5</span>, <span>Web Development</span></p>
</footer>
</article>
<aside>
<h3>Related Links</h3>
<ul>
<li><a href="/related-article">Related Article</a></li>
</ul>
</aside>
</main>
<footer>
<p>© 2024 My Blog. All rights reserved.</p>
</footer>
</body>
</html>
Key semantic elements:
-
<header>
- Page or section header -
<nav>
- Navigation links -
<main>
- Primary content area -
<article>
- Independent content pieces -
<section>
- Thematic content groupings -
<aside>
- Sidebar or complementary content -
<footer>
- Page or section footer
2.2. Enhanced Form Elements
HTML5 provides new input types and validation:
<form>
<fieldset>
<legend>Contact Information</legend>
<label for="email">Email:</label>
<input type="email" id="email" required>
<label for="phone">Phone:</label>
<input type="tel" id="phone" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}">
<label for="website">Website:</label>
<input type="url" id="website">
<label for="birthdate">Birth Date:</label>
<input type="date" id="birthdate">
<label for="age">Age:</label>
<input type="number" id="age" min="18" max="100">
<label for="volume">Volume:</label>
<input type="range" id="volume" min="0" max="100" value="50">
<label for="color">Favorite Color:</label>
<input type="color" id="color">
<button type="submit">Submit</button>
</fieldset>
</form>
New input types include:
email
, url
, tel
, search
, date
, time
, number
, range
, color
2.3. Native Media Support
HTML5 includes built-in audio and video support:
<!-- Audio Element -->
<audio controls>
<source src="audio.mp3" type="audio/mpeg">
<source src="audio.ogg" type="audio/ogg">
Your browser does not support the audio element.
</audio>
<!-- Video Element -->
<video width="320" height="240" controls poster="thumbnail.jpg">
<source src="movie.mp4" type="video/mp4">
<source src="movie.webm" type="video/webm">
<track kind="subtitles" src="subtitles.vtt" srclang="en" label="English">
Your browser does not support the video element.
</video>
Benefits: * No plugin dependencies * Programmatic control via JavaScript * Accessibility features built-in * Mobile device optimization
2.4. HTML5 APIs
HTML5 provides JavaScript APIs for enhanced functionality:
- Canvas API
-
2D and 3D graphics rendering
<canvas id="myCanvas" width="200" height="100"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// Draw a rectangle
ctx.fillStyle = '#FF0000';
ctx.fillRect(10, 10, 50, 50);
// Draw a circle
ctx.beginPath();
ctx.arc(100, 50, 25, 0, 2 * Math.PI);
ctx.fillStyle = '#0000FF';
ctx.fill();
</script>
- Web Storage
-
Client-side data persistence
// Local Storage - persists after browser restart
localStorage.setItem('user', JSON.stringify({name: 'John'}));
const user = JSON.parse(localStorage.getItem('user'));
// Session Storage - cleared when tab closes
sessionStorage.setItem('temp', 'value');
- Geolocation
-
Access user location
navigator.geolocation.getCurrentPosition(
(position) => {
console.log('Lat:', position.coords.latitude);
console.log('Lng:', position.coords.longitude);
},
(error) => console.error('Location error:', error)
);
2.5. Accessibility Features
HTML5 improves web accessibility:
- ARIA Attributes
-
Provide additional semantic information
<button aria-label="Close dialog" aria-expanded="false">×</button>
<div role="alert" aria-live="polite">Status message</div>
- Semantic Structure
-
Screen readers understand content hierarchy
- Keyboard Navigation
-
tabindex
and focus management
2.6. HTML5 Best Practices
- Use Semantic Elements
-
Choose elements based on meaning, not appearance
- Validate Markup
-
Use W3C validator to ensure standards compliance
- Optimize for Performance
-
Minimize DOM depth and use appropriate elements
- Progressive Enhancement
-
Ensure functionality without JavaScript
Example of well-structured HTML5:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Complete HTML5 example with modern features">
<title>Complete HTML5 Example</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
max-width: 1200px;
margin: 0 auto;
padding: 1rem;
}
header, footer {
background: #f5f5f5;
padding: 1rem;
border-radius: 0.5rem;
}
nav ul {
list-style: none;
display: flex;
gap: 1rem;
padding: 0;
}
main {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 2rem;
margin: 2rem 0;
}
@media (max-width: 768px) {
main {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<header>
<h1>Modern Web Development</h1>
<nav aria-label="Main navigation">
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#tutorials">Tutorials</a></li>
<li><a href="#about">About</a></li>
</ul>
</nav>
</header>
<main>
<section>
<article>
<header>
<h2>HTML5 Features</h2>
<time datetime="2024-12-28">Published on December 28, 2024</time>
</header>
<p>HTML5 brings semantic meaning to web documents through new elements and APIs.</p>
<details>
<summary>More information</summary>
<p>HTML5 includes new form controls, multimedia elements, and JavaScript APIs.</p>
</details>
</article>
<section aria-labelledby="contact-form">
<h3 id="contact-form">Contact Form</h3>
<form>
<label for="name">Name (required):</label>
<input type="text" id="name" required aria-describedby="name-help">
<small id="name-help">Please enter your full name</small>
<label for="email">Email:</label>
<input type="email" id="email" required>
<button type="submit">Send Message</button>
</form>
</section>
</section>
<aside aria-labelledby="sidebar-heading">
<h3 id="sidebar-heading">Related Topics</h3>
<ul>
<li><a href="/css3">CSS3 Guide</a></li>
<li><a href="/javascript">JavaScript ES6+</a></li>
<li><a href="/accessibility">Web Accessibility</a></li>
</ul>
</aside>
</main>
<footer>
<p>© 2024 Web Standards Tutorial. All rights reserved.</p>
<p>
<a href="/privacy">Privacy Policy</a> |
<a href="/terms">Terms of Service</a>
</p>
</footer>
<script>
// Progressive enhancement example
document.querySelector('form')?.addEventListener('submit', (e) => {
e.preventDefault();
alert('Form would be submitted via JavaScript!');
});
</script>
</body>
</html>
3. CSS3 - Modern Styling
CSS3 provides powerful styling capabilities including responsive layouts, animations, and advanced selectors. Modern CSS enables developers to create sophisticated designs without relying on external frameworks.
3.1. Modern Layout Systems
CSS3 introduces flexible layout systems that replace older float-based approaches.
- CSS Grid
-
Two-dimensional layout system for complex designs
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-gap: 1rem;
padding: 1rem;
}
.grid-item {
background: #f0f0f0;
padding: 1rem;
border-radius: 0.5rem;
}
/* Named Grid Lines */
.layout {
display: grid;
grid-template-columns:
[sidebar-start] 250px
[sidebar-end main-start] 1fr
[main-end];
grid-template-rows:
[header-start] 60px
[header-end content-start] 1fr
[content-end footer-start] 40px
[footer-end];
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: content / main; }
.footer { grid-area: footer; }
- Flexbox
-
One-dimensional layout for component arrangement
.flex-container {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
.flex-item {
flex: 1 1 200px; /* grow shrink basis */
min-width: 0; /* Allow items to shrink below content size */
}
/* Navigation Menu */
.nav {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.nav li {
flex: 0 0 auto;
}
.nav a {
display: block;
padding: 0.5rem 1rem;
text-decoration: none;
}
/* Card Layout */
.card {
display: flex;
flex-direction: column;
height: 100%;
}
.card-content {
flex: 1; /* Takes remaining space */
}
.card-actions {
flex: 0 0 auto; /* Don't grow or shrink */
margin-top: auto;
}
3.2. Responsive Design
CSS3 media queries enable adaptive designs across devices:
/* Mobile First Approach */
.container {
width: 100%;
padding: 0 1rem;
}
/* Tablet styles */
@media (min-width: 768px) {
.container {
max-width: 750px;
margin: 0 auto;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2rem;
}
}
/* Desktop styles */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
}
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* Large desktop */
@media (min-width: 1440px) {
.container {
max-width: 1400px;
}
}
/* Dark mode preference */
@media (prefers-color-scheme: dark) {
body {
background-color: #1a1a1a;
color: #ffffff;
}
}
/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Key responsive design principles:
-
Mobile-first approach: Design for small screens, then enhance
-
Flexible units: Use
rem
,em
,vh
,vw
instead of fixed pixels -
Fluid grids: Layouts that adapt to container size
-
Flexible images:
max-width: 100%
for scalable images
3.3. Animations and Transitions
CSS3 provides native animation capabilities:
- Transitions
-
Smooth property changes
.button {
background-color: blue;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: red;
}
- Keyframe Animations
-
Complex multi-step animations
@keyframes slideIn {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-30px);
}
60% {
transform: translateY(-15px);
}
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
/* Usage */
.slide-in {
animation: slideIn 0.5s ease-out;
}
.bounce {
animation: bounce 2s infinite;
}
.pulse {
animation: pulse 1s ease-in-out infinite alternate;
}
/* Loading spinner */
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
3.4. Advanced Selectors
CSS3 introduces powerful selector types:
- Attribute Selectors
input[type="email"] { border-color: blue; }
a[href^="https"] { color: green; }
div[class*="card"] { padding: 1rem; }
- Pseudo-classes
:nth-child(odd) { background: #f0f0f0; }
:not(.active) { opacity: 0.5; }
:focus-visible { outline: 2px solid blue; }
- Pseudo-elements
::before { content: "★"; }
::after { content: ""; display: block; clear: both; }
::first-letter { font-size: 2em; float: left; }
3.5. CSS Custom Properties (Variables)
CSS variables enable maintainable and dynamic styling:
/* Global variables */
:root {
--primary-color: #3498db;
--secondary-color: #2c3e50;
--accent-color: #e74c3c;
--text-color: #333333;
--background-color: #ffffff;
--border-radius: 0.5rem;
--spacing-unit: 1rem;
--font-family: system-ui, -apple-system, sans-serif;
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
--text-color: #ffffff;
--background-color: #1a1a1a;
--primary-color: #5dade2;
--secondary-color: #34495e;
}
}
/* Usage */
.card {
background-color: var(--background-color);
color: var(--text-color);
border-radius: var(--border-radius);
padding: calc(var(--spacing-unit) * 2);
font-family: var(--font-family);
}
.button {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: var(--border-radius);
padding: var(--spacing-unit);
font-family: var(--font-family);
}
.button:hover {
background-color: var(--accent-color);
}
/* Component-specific variables */
.sidebar {
--sidebar-width: 250px;
--sidebar-background: var(--secondary-color);
width: var(--sidebar-width);
background-color: var(--sidebar-background);
}
Benefits: * Reduce code duplication * Enable theming and dark mode * JavaScript can modify values dynamically * Cascade and inheritance support
3.6. Modern CSS Features
- Container Queries
-
Style based on container size, not viewport
.card {
container-type: inline-size;
}
@container (min-width: 400px) {
.card h2 {
font-size: 2rem;
}
}
- CSS Cascade Layers
-
Control specificity and cascade order
@layer base, components, utilities;
@layer base {
h1 { font-size: 2rem; }
}
@layer components {
.heading { font-size: 3rem; }
}
- Subgrid
-
Nested grid layouts
.parent {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.child {
display: grid;
grid-template-columns: subgrid;
}
3.7. CSS Best Practices
- Organized Architecture
-
Use methodologies like BEM or utility-first approaches
- Performance Optimization
-
-
Minimize CSS file sizes
-
Avoid complex selectors
-
Use CSS containment for performance
-
Leverage browser caching
-
- Maintainability
-
-
Use meaningful class names
-
Group related styles
-
Comment complex code
-
Follow consistent formatting
-
Example of well-structured CSS:
/* Modern CSS Example - Component-based styling */
/* Reset and base styles */
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
line-height: 1.5;
color: #333;
}
/* Layout components */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
/* Card component */
.card {
background: white;
border-radius: 0.5rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
.card__header {
padding: 1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.card__title {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
}
.card__content {
padding: 1rem;
}
.card__actions {
padding: 0 1rem 1rem;
display: flex;
gap: 0.5rem;
}
/* Button component */
.button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.5rem 1rem;
border: none;
border-radius: 0.25rem;
font-size: 0.875rem;
font-weight: 500;
text-decoration: none;
cursor: pointer;
transition: all 0.2s ease;
}
.button--primary {
background-color: #3b82f6;
color: white;
}
.button--primary:hover {
background-color: #2563eb;
}
.button--secondary {
background-color: #e5e7eb;
color: #374151;
}
.button--secondary:hover {
background-color: #d1d5db;
}
/* Utility classes */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
4. JavaScript ES6+ - Modern Web Scripting
JavaScript ES6+ provides modern syntax and powerful features for building interactive web applications. Understanding current JavaScript standards is essential for modern web development.
4.1. Modern JavaScript Syntax
- Arrow Functions
-
Concise function syntax with lexical
this
binding
// Traditional function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
// Arrow function with multiple statements
const processUser = (user) => {
const processed = { ...user, processed: true };
return processed;
};
- Template Literals
-
String interpolation and multi-line strings
const name = 'World';
const message = `Hello, ${name}!
This is a multi-line string
with embedded expressions: ${2 + 3}`;
- Destructuring
-
Extract values from arrays and objects
// Object destructuring
const { name, age, email = 'unknown' } = user;
// Array destructuring
const [first, second, ...rest] = numbers;
// Function parameter destructuring
const processUser = ({ name, age }) => {
console.log(`${name} is ${age} years old`);
};
4.2. ES6 Modules
Modern JavaScript uses modules for code organization:
// math.js
export const PI = 3.14159;
export const add = (a, b) => a + b;
export default function multiply(a, b) {
return a * b;
}
// app.js
import multiply, { PI, add } from './math.js';
import * as math from './math.js';
console.log(add(2, 3)); // 5
console.log(multiply(4, 5)); // 20
4.3. Asynchronous JavaScript
- Promises
-
Handle asynchronous operations
// Creating a Promise
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data loaded');
}, 1000);
});
};
// Using Promises
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
- Async/Await
-
Cleaner asynchronous code
async function loadUserData(userId) {
try {
const user = await fetch(`/api/users/${userId}`);
const userData = await user.json();
const posts = await fetch(`/api/users/${userId}/posts`);
const postsData = await posts.json();
return { user: userData, posts: postsData };
} catch (error) {
console.error('Failed to load user data:', error);
throw error;
}
}
// Usage
loadUserData(123)
.then(data => console.log(data))
.catch(error => console.error(error));
4.4. Classes and Objects
Modern JavaScript provides class-based syntax:
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
// Method
greet() {
return `Hello, I'm ${this.name}`;
}
// Getter
get displayName() {
return this.name.toUpperCase();
}
// Setter
set displayName(value) {
this.name = value.toLowerCase();
}
// Static method
static fromObject(obj) {
return new User(obj.name, obj.email);
}
}
// Inheritance
class AdminUser extends User {
constructor(name, email, permissions) {
super(name, email);
this.permissions = permissions;
}
greet() {
return `${super.greet()} (Admin)`;
}
}
const user = new User('John', 'john@example.com');
const admin = new AdminUser('Jane', 'jane@example.com', ['read', 'write']);
4.5. Modern Web APIs
- Fetch API
-
Modern HTTP requests
// GET request
const response = await fetch('/api/data');
const data = await response.json();
// POST request
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'John', email: 'john@example.com' })
});
// Error handling
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
- DOM Manipulation
-
Modern element selection and manipulation
// Element selection
const button = document.querySelector('.submit-button');
const items = document.querySelectorAll('.list-item');
// Event handling
button.addEventListener('click', (event) => {
event.preventDefault();
console.log('Button clicked!');
});
// Creating elements
const newElement = document.createElement('div');
newElement.className = 'dynamic-content';
newElement.textContent = 'Dynamic content';
document.body.appendChild(newElement);
// Modern event handling with delegation
document.addEventListener('click', (event) => {
if (event.target.matches('.dynamic-button')) {
console.log('Dynamic button clicked!');
}
});
4.6. Advanced Features
- Array Methods
-
Functional programming with arrays
const numbers = [1, 2, 3, 4, 5];
// Transform data
const doubled = numbers.map(n => n * 2);
const filtered = numbers.filter(n => n > 3);
const sum = numbers.reduce((acc, n) => acc + n, 0);
// Find elements
const found = numbers.find(n => n > 3);
const exists = numbers.some(n => n > 4);
const all = numbers.every(n => n > 0);
- Object Methods
-
Working with objects
const user = { name: 'John', age: 30, city: 'New York' };
// Get keys, values, entries
const keys = Object.keys(user);
const values = Object.values(user);
const entries = Object.entries(user);
// Object composition
const defaults = { theme: 'light', language: 'en' };
const settings = { ...defaults, ...userSettings };
// Object.assign
const merged = Object.assign({}, defaults, userSettings);
4.7. Best Practices
- Error Handling
-
Proper error management
// Custom error classes
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
// Comprehensive error handling
async function processForm(formData) {
try {
validateFormData(formData);
const result = await submitForm(formData);
return { success: true, data: result };
} catch (error) {
if (error instanceof ValidationError) {
return { success: false, error: error.message, field: error.field };
}
console.error('Unexpected error:', error);
return { success: false, error: 'An unexpected error occurred' };
}
}
- Performance Optimization
// Debouncing
const debounce = (func, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(null, args), delay);
};
};
// Usage
const debouncedSearch = debounce((query) => {
performSearch(query);
}, 300);
// Throttling
const throttle = (func, limit) => {
let inThrottle;
return (...args) => {
if (!inThrottle) {
func.apply(null, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
};
5. HTTP/2 and Modern Web Protocols
HTTP/2 represents a major evolution in web communication, providing improved performance and new capabilities. Understanding modern HTTP features is crucial for optimizing web application performance.
5.1. HTTP Protocol Evolution
- HTTP/1.1
-
Traditional protocol with text-based headers and single request per connection
- HTTP/2
-
Binary protocol with multiplexing, server push, and header compression
- HTTP/3
-
Latest standard built on QUIC protocol for improved performance and security
5.2. HTTP/2 Key Features
- Multiplexing
-
Multiple requests over a single connection
Traditional HTTP/1.1 requires multiple connections for parallel requests, causing overhead. HTTP/2 allows unlimited parallel requests over one connection.
// Multiple requests benefit from HTTP/2 multiplexing
const promises = [
fetch('/api/user'),
fetch('/api/posts'),
fetch('/api/comments'),
fetch('/api/notifications')
];
const results = await Promise.all(promises);
- Server Push
-
Proactive resource delivery
Server can push resources before the client requests them:
# Server configuration example (Nginx)
location / {
http2_push /css/main.css;
http2_push /js/app.js;
http2_push /images/logo.png;
}
- Header Compression
-
HPACK algorithm reduces header overhead
HTTP/2 compresses headers using HPACK, significantly reducing bandwidth usage for requests with similar headers.
5.3. HTTP Methods and Usage
Modern web applications use various HTTP methods for different purposes:
// RESTful API examples
const api = {
// GET - Retrieve data
async getUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json();
},
// POST - Create new resource
async createUser(userData) {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
return response.json();
},
// PUT - Update entire resource
async updateUser(id, userData) {
const response = await fetch(`/api/users/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
return response.json();
},
// PATCH - Partial update
async updateUserEmail(id, email) {
const response = await fetch(`/api/users/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
return response.json();
},
// DELETE - Remove resource
async deleteUser(id) {
const response = await fetch(`/api/users/${id}`, {
method: 'DELETE'
});
return response.ok;
}
};
5.4. HTTP Status Codes
Understanding status codes helps with proper error handling:
- Success (2xx)
-
-
200 OK
- Request successful -
201 Created
- Resource created -
204 No Content
- Success, no response body
-
- Redirection (3xx)
-
-
301 Moved Permanently
- Permanent redirect -
302 Found
- Temporary redirect -
304 Not Modified
- Cached version is current
-
- Client Error (4xx)
-
-
400 Bad Request
- Invalid request -
401 Unauthorized
- Authentication required -
403 Forbidden
- Access denied -
404 Not Found
- Resource doesn’t exist -
429 Too Many Requests
- Rate limiting
-
- Server Error (5xx)
-
-
500 Internal Server Error
- Server error -
502 Bad Gateway
- Gateway error -
503 Service Unavailable
- Service temporarily down
-
5.5. Important HTTP Headers
- Security Headers
Content-Security-Policy: default-src 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
- Caching Headers
Cache-Control: public, max-age=31536000, immutable
ETag: "abc123"
Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT
- CORS Headers
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
5.6. HTTPS and Security
- TLS/SSL
-
Encryption for secure communication
All modern web applications should use HTTPS:
-
Encrypts data in transit
-
Prevents man-in-the-middle attacks
-
Required for modern web APIs
-
Improves SEO rankings
- Certificate Management
-
Free certificates from Let’s Encrypt
- HTTP Strict Transport Security (HSTS)
-
Forces HTTPS connections
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
5.7. Performance Optimization
- Connection Management
// Connection pooling with fetch
const controller = new AbortController();
// Cancel requests when needed
setTimeout(() => controller.abort(), 5000);
const response = await fetch('/api/data', {
signal: controller.signal
});
- Request Optimization
// Batch multiple operations
const batchRequest = async (operations) => {
const response = await fetch('/api/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ operations })
});
return response.json();
};
// Use with GraphQL or custom batch endpoints
const results = await batchRequest([
{ type: 'getUser', id: 1 },
{ type: 'getPosts', userId: 1 },
{ type: 'getComments', postId: 123 }
]);
- Response Compression
-
Enable gzip/brotli compression on server
# Server configuration enables compression
Content-Encoding: br
Content-Type: application/json
Vary: Accept-Encoding
5.8. Modern HTTP Features
- WebSocket Upgrade
-
Real-time communication
const socket = new WebSocket('wss://example.com/socket');
socket.onopen = () => {
console.log('Connected to WebSocket');
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
socket.send(JSON.stringify({ type: 'message', content: 'Hello!' }));
- Server-Sent Events
-
One-way real-time updates
const eventSource = new EventSource('/api/events');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
updateUI(data);
};
eventSource.addEventListener('user-update', (event) => {
const userData = JSON.parse(event.data);
updateUserDisplay(userData);
});
5.9. Best Practices
- Error Handling
-
Comprehensive HTTP error management
async function apiRequest(url, options = {}) {
try {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const errorData = await response.text();
throw new Error(`HTTP ${response.status}: ${errorData}`);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request was cancelled');
} else if (error.name === 'TypeError') {
console.error('Network error:', error.message);
} else {
console.error('API error:', error.message);
}
throw error;
}
}
- Rate Limiting
-
Implement client-side rate limiting
class RateLimiter {
constructor(maxRequests, timeWindow) {
this.maxRequests = maxRequests;
this.timeWindow = timeWindow;
this.requests = [];
}
async throttle() {
const now = Date.now();
this.requests = this.requests.filter(time => now - time < this.timeWindow);
if (this.requests.length >= this.maxRequests) {
const oldestRequest = Math.min(...this.requests);
const waitTime = this.timeWindow - (now - oldestRequest);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
this.requests.push(now);
}
}
const limiter = new RateLimiter(10, 60000); // 10 requests per minute
async function apiCall(url) {
await limiter.throttle();
return fetch(url);
}
6. Modern Web APIs
Modern browsers provide extensive APIs that enable rich, native-like web experiences. These APIs provide access to device capabilities and advanced web functionality.
6.1. Storage APIs
- Web Storage
-
Client-side key-value storage
// Local Storage - persistent across browser sessions
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
localStorage.removeItem('theme');
localStorage.clear();
// Session Storage - cleared when tab closes
sessionStorage.setItem('temp-data', JSON.stringify(data));
const tempData = JSON.parse(sessionStorage.getItem('temp-data'));
- IndexedDB
-
Browser-based NoSQL database
// Open database
const request = indexedDB.open('MyDB', 1);
request.onsuccess = (event) => {
const db = event.target.result;
// Create transaction
const transaction = db.transaction(['users'], 'readwrite');
const store = transaction.objectStore('users');
// Add data
const user = { id: 1, name: 'John', email: 'john@example.com' };
store.add(user);
// Get data
const getRequest = store.get(1);
getRequest.onsuccess = () => {
console.log('User:', getRequest.result);
};
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
const store = db.createObjectStore('users', { keyPath: 'id' });
store.createIndex('email', 'email', { unique: true });
};
6.2. Geolocation API
Access user’s geographical location:
// Get current position
navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude, accuracy } = position.coords;
console.log(`Location: ${latitude}, ${longitude} (±${accuracy}m)`);
// Use location data
updateMap(latitude, longitude);
findNearbyServices(latitude, longitude);
},
(error) => {
switch(error.code) {
case error.PERMISSION_DENIED:
console.error('User denied location access');
break;
case error.POSITION_UNAVAILABLE:
console.error('Location information unavailable');
break;
case error.TIMEOUT:
console.error('Location request timed out');
break;
}
},
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
// Watch position changes
const watchId = navigator.geolocation.watchPosition(
(position) => {
updateUserLocation(position.coords);
},
(error) => console.error('Location error:', error),
{ enableHighAccuracy: true }
);
// Stop watching
navigator.geolocation.clearWatch(watchId);
6.3. Media APIs
- MediaDevices
-
Access camera and microphone
// Get user media
async function startCamera() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { width: 1280, height: 720 },
audio: true
});
const video = document.getElementById('video');
video.srcObject = stream;
return stream;
} catch (error) {
console.error('Error accessing media devices:', error);
}
}
// Screen capture
async function startScreenShare() {
try {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true
});
const video = document.getElementById('screen-video');
video.srcObject = stream;
return stream;
} catch (error) {
console.error('Error accessing display media:', error);
}
}
// List available devices
async function listDevices() {
const devices = await navigator.mediaDevices.enumerateDevices();
devices.forEach((device) => {
console.log(`${device.kind}: ${device.label} (${device.deviceId})`);
});
}
6.4. Notifications API
Display system notifications:
// Request permission
async function requestNotificationPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('Notifications enabled');
} else {
console.log('Notifications denied');
}
return permission;
}
// Send notification
function sendNotification(title, options = {}) {
if (Notification.permission === 'granted') {
const notification = new Notification(title, {
body: options.body || 'Default message',
icon: options.icon || '/icon.png',
badge: options.badge || '/badge.png',
tag: options.tag || 'default',
requireInteraction: options.persistent || false,
actions: options.actions || []
});
notification.onclick = () => {
window.focus();
notification.close();
};
// Auto-close after 5 seconds
setTimeout(() => notification.close(), 5000);
return notification;
}
}
// Usage
sendNotification('New Message', {
body: 'You have received a new message',
icon: '/message-icon.png',
tag: 'message',
actions: [
{ action: 'reply', title: 'Reply' },
{ action: 'mark-read', title: 'Mark as Read' }
]
});
6.5. Intersection Observer API
Efficiently observe element visibility:
// Lazy loading images
const imageObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
imageObserver.unobserve(img);
}
});
},
{
threshold: 0.1,
rootMargin: '50px'
}
);
// Observe images
document.querySelectorAll('img[data-src]').forEach((img) => {
imageObserver.observe(img);
});
// Infinite scrolling
const sentinelObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
loadMoreContent();
}
});
},
{ threshold: 1.0 }
);
sentinelObserver.observe(document.querySelector('.loading-sentinel'));
6.6. ResizeObserver API
Monitor element size changes:
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
const { width, height } = entry.contentRect;
// Responsive behavior based on element size
if (width < 400) {
entry.target.classList.add('compact');
} else {
entry.target.classList.remove('compact');
}
// Update charts or layouts
if (entry.target.classList.contains('chart')) {
updateChartSize(entry.target, width, height);
}
});
});
// Observe elements
document.querySelectorAll('.responsive-component').forEach((element) => {
resizeObserver.observe(element);
});
6.7. Web Components APIs
- Custom Elements
-
Define custom HTML elements
class UserCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
this.addEventListener('click', this.handleClick);
}
disconnectedCallback() {
this.removeEventListener('click', this.handleClick);
}
static get observedAttributes() {
return ['name', 'email', 'avatar'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.render();
}
}
render() {
const name = this.getAttribute('name') || 'Unknown';
const email = this.getAttribute('email') || '';
const avatar = this.getAttribute('avatar') || '/default-avatar.png';
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
border: 1px solid #ccc;
border-radius: 8px;
padding: 1rem;
margin: 0.5rem;
}
.avatar {
width: 50px;
height: 50px;
border-radius: 50%;
}
.info {
margin-left: 1rem;
}
.container {
display: flex;
align-items: center;
}
</style>
<div class="container">
<img class="avatar" src="${avatar}" alt="${name}">
<div class="info">
<h3>${name}</h3>
<p>${email}</p>
</div>
</div>
`;
}
handleClick() {
this.dispatchEvent(new CustomEvent('user-selected', {
detail: {
name: this.getAttribute('name'),
email: this.getAttribute('email')
},
bubbles: true
}));
}
}
// Define custom element
customElements.define('user-card', UserCard);
// Usage in HTML
// <user-card name="John Doe" email="john@example.com" avatar="/john.jpg"></user-card>
6.8. Web Workers API
Run JavaScript in background threads:
// main.js
const worker = new Worker('/js/worker.js');
// Send data to worker
worker.postMessage({
command: 'process',
data: largeDataArray
});
// Receive results from worker
worker.onmessage = (event) => {
const { command, result } = event.data;
if (command === 'progress') {
updateProgressBar(result.percentage);
} else if (command === 'complete') {
displayResults(result.data);
worker.terminate(); // Clean up
}
};
worker.onerror = (error) => {
console.error('Worker error:', error);
};
// worker.js
self.onmessage = (event) => {
const { command, data } = event.data;
if (command === 'process') {
const total = data.length;
const results = [];
for (let i = 0; i < total; i++) {
// Intensive computation
const processed = processItem(data[i]);
results.push(processed);
// Report progress every 100 items
if (i % 100 === 0) {
self.postMessage({
command: 'progress',
result: { percentage: (i / total) * 100 }
});
}
}
// Send final results
self.postMessage({
command: 'complete',
result: { data: results }
});
}
};
function processItem(item) {
// Simulate heavy computation
let result = item;
for (let i = 0; i < 1000000; i++) {
result = Math.sin(result);
}
return result;
}
6.9. Service Worker API
Enable offline functionality and background processing:
// Register service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
console.log('SW registered:', registration);
})
.catch((error) => {
console.error('SW registration failed:', error);
});
}
// sw.js (Service Worker)
const CACHE_NAME = 'my-app-v1';
const urlsToCache = [
'/',
'/css/main.css',
'/js/app.js',
'/images/logo.png'
];
// Install event - cache resources
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
);
});
// Fetch event - serve from cache
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
// Return cached version or fetch from network
return response || fetch(event.request);
})
);
});
// Background sync
self.addEventListener('sync', (event) => {
if (event.tag === 'background-sync') {
event.waitUntil(syncData());
}
});
// Push notifications
self.addEventListener('push', (event) => {
const options = {
body: event.data.text(),
icon: '/icon.png',
badge: '/badge.png'
};
event.waitUntil(
self.registration.showNotification('Push Notification', options)
);
});
7. Progressive Web Apps (PWAs)
Progressive Web Apps combine the best features of web and native applications. PWAs provide app-like experiences with offline functionality, push notifications, and installability.
7.1. PWA Characteristics
- Progressive
-
Works for every user, regardless of browser choice
- Responsive
-
Fits any form factor: desktop, mobile, tablet
- Connectivity Independent
-
Enhanced with service workers to work offline
- App-like
-
Feels like an app with app-style interactions and navigation
- Secure
-
Served via HTTPS to prevent tampering
- Discoverable
-
Identifiable as "applications" via W3C manifests
- Re-engageable
-
Push notifications keep users engaged
- Installable
-
Can be installed on device home screen
- Linkable
-
Easily shared via URL without complex installation
7.2. Web App Manifest
The manifest provides metadata about your web application:
{
"name": "My Progressive Web App",
"short_name": "MyPWA",
"description": "A sample progressive web application",
"start_url": "/",
"display": "standalone",
"orientation": "portrait-primary",
"theme_color": "#000000",
"background_color": "#ffffff",
"scope": "/",
"lang": "en-US",
"dir": "ltr",
"icons": [
{
"src": "/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"shortcuts": [
{
"name": "New Message",
"short_name": "New",
"description": "Create a new message",
"url": "/compose",
"icons": [
{
"src": "/icons/compose.png",
"sizes": "96x96"
}
]
}
],
"categories": ["productivity", "communication"],
"screenshots": [
{
"src": "/screenshots/desktop.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide"
},
{
"src": "/screenshots/mobile.png",
"sizes": "375x667",
"type": "image/png",
"form_factor": "narrow"
}
]
}
Link the manifest in your HTML:
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#000000">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="MyPWA">
7.3. Service Worker Implementation
Service workers enable offline functionality and background processing:
// Register service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
console.log('SW registered: ', registration);
// Check for updates
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// New version available
showUpdateNotification();
}
});
});
})
.catch((registrationError) => {
console.log('SW registration failed: ', registrationError);
});
});
}
// Show update notification
function showUpdateNotification() {
const notification = document.createElement('div');
notification.innerHTML = `
<div class="update-notification">
<p>A new version is available!</p>
<button onclick="updateApp()">Update</button>
<button onclick="dismissUpdate()">Later</button>
</div>
`;
document.body.appendChild(notification);
}
function updateApp() {
navigator.serviceWorker.getRegistration().then((registration) => {
if (registration.waiting) {
registration.waiting.postMessage({ type: 'SKIP_WAITING' });
}
});
location.reload();
}
Advanced Service Worker:
// sw.js
const CACHE_NAME = 'pwa-cache-v1';
const RUNTIME_CACHE = 'runtime-cache';
// Resources to cache immediately
const PRECACHE_URLS = [
'/',
'/css/main.css',
'/js/app.js',
'/offline.html',
'/icons/icon-192x192.png'
];
// Install event
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(PRECACHE_URLS))
.then(() => self.skipWaiting())
);
});
// Activate event
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((cacheName) => cacheName !== CACHE_NAME && cacheName !== RUNTIME_CACHE)
.map((cacheName) => caches.delete(cacheName))
);
}).then(() => self.clients.claim())
);
});
// Fetch event with caching strategies
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Handle API requests
if (url.pathname.startsWith('/api/')) {
event.respondWith(networkFirst(request));
return;
}
// Handle static assets
if (request.destination === 'image' || request.destination === 'script' || request.destination === 'style') {
event.respondWith(cacheFirst(request));
return;
}
// Handle pages
event.respondWith(staleWhileRevalidate(request));
});
// Caching strategies
async function networkFirst(request) {
try {
const response = await fetch(request);
const cache = await caches.open(RUNTIME_CACHE);
cache.put(request, response.clone());
return response;
} catch (error) {
const cachedResponse = await caches.match(request);
return cachedResponse || new Response('Offline', { status: 503 });
}
}
async function cacheFirst(request) {
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
try {
const response = await fetch(request);
const cache = await caches.open(RUNTIME_CACHE);
cache.put(request, response.clone());
return response;
} catch (error) {
return new Response('Not found', { status: 404 });
}
}
async function staleWhileRevalidate(request) {
const cache = await caches.open(RUNTIME_CACHE);
const cachedResponse = await cache.match(request);
const networkRequest = fetch(request).then((response) => {
cache.put(request, response.clone());
return response;
}).catch(() => {
// Return offline page for navigation requests
if (request.mode === 'navigate') {
return cache.match('/offline.html');
}
throw error;
});
return cachedResponse || networkRequest;
}
// Background sync
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-data') {
event.waitUntil(syncOfflineData());
}
});
async function syncOfflineData() {
// Sync data that was stored while offline
const offlineActions = await getOfflineActions();
for (const action of offlineActions) {
try {
await fetch('/api/sync', {
method: 'POST',
body: JSON.stringify(action),
headers: { 'Content-Type': 'application/json' }
});
await removeOfflineAction(action.id);
} catch (error) {
console.error('Sync failed for action:', action.id);
}
}
}
7.4. App Installation
Handle PWA installation prompts:
let deferredPrompt;
const installButton = document.getElementById('install-button');
// Listen for beforeinstallprompt event
window.addEventListener('beforeinstallprompt', (event) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
event.preventDefault();
// Store the event so it can be triggered later
deferredPrompt = event;
// Show install button
installButton.style.display = 'block';
installButton.addEventListener('click', () => {
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the A2HS prompt');
trackEvent('pwa_install', 'accepted');
} else {
console.log('User dismissed the A2HS prompt');
trackEvent('pwa_install', 'dismissed');
}
deferredPrompt = null;
installButton.style.display = 'none';
});
});
});
// Listen for app installed event
window.addEventListener('appinstalled', (event) => {
console.log('PWA was installed');
trackEvent('pwa_install', 'completed');
// Hide install button
installButton.style.display = 'none';
// Show welcome message
showWelcomeMessage();
});
// Check if app is running in standalone mode
function isStandalone() {
return window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true;
}
if (isStandalone()) {
// App is running in standalone mode
document.body.classList.add('standalone');
}
7.5. Push Notifications
Implement push notifications for user engagement:
// Request notification permission
async function requestNotificationPermission() {
if (!('Notification' in window)) {
console.warn('Notifications not supported');
return false;
}
if (Notification.permission === 'granted') {
return true;
}
if (Notification.permission === 'denied') {
console.warn('Notifications denied by user');
return false;
}
const permission = await Notification.requestPermission();
return permission === 'granted';
}
// Subscribe to push notifications
async function subscribeToPushNotifications() {
const hasPermission = await requestNotificationPermission();
if (!hasPermission) return;
const registration = await navigator.serviceWorker.ready;
try {
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
// Send subscription to server
await fetch('/api/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription)
});
console.log('Push subscription successful');
} catch (error) {
console.error('Push subscription failed:', error);
}
}
// Handle push events in service worker
self.addEventListener('push', (event) => {
const data = event.data ? event.data.json() : {};
const options = {
body: data.body || 'New notification',
icon: '/icons/icon-192x192.png',
badge: '/icons/badge-72x72.png',
data: data.data || {},
actions: [
{
action: 'view',
title: 'View',
icon: '/icons/view.png'
},
{
action: 'dismiss',
title: 'Dismiss',
icon: '/icons/dismiss.png'
}
],
requireInteraction: true,
tag: data.tag || 'default'
};
event.waitUntil(
self.registration.showNotification(data.title || 'Notification', options)
);
});
// Handle notification clicks
self.addEventListener('notificationclick', (event) => {
event.notification.close();
const action = event.action;
const data = event.notification.data;
event.waitUntil(
clients.openWindow(data.url || '/')
);
});
7.6. PWA Best Practices
- Performance
-
-
Implement efficient caching strategies
-
Optimize critical rendering path
-
Use resource prioritization
-
Minimize main thread work
-
- User Experience
-
-
Provide meaningful offline experiences
-
Show connection status indicators
-
Handle failed requests gracefully
-
Implement smooth animations and transitions
-
- Security
-
-
Serve over HTTPS
-
Validate all inputs
-
Implement Content Security Policy
-
Regular security audits
-
- Testing
-
-
Test offline functionality
-
Validate on multiple devices
-
Use Lighthouse PWA audit
-
Monitor Core Web Vitals
-
Example PWA architecture:
class PWAApp {
constructor() {
this.isOnline = navigator.onLine;
this.offlineQueue = [];
this.init();
}
async init() {
// Register service worker
await this.registerServiceWorker();
// Setup offline handling
this.setupOfflineHandling();
// Setup push notifications
await this.setupPushNotifications();
// Setup installation prompt
this.setupInstallPrompt();
}
async registerServiceWorker() {
if ('serviceWorker' in navigator) {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('Service Worker registered:', registration);
}
}
setupOfflineHandling() {
window.addEventListener('online', () => {
this.isOnline = true;
this.processOfflineQueue();
this.showConnectionStatus('online');
});
window.addEventListener('offline', () => {
this.isOnline = false;
this.showConnectionStatus('offline');
});
}
async processOfflineQueue() {
while (this.offlineQueue.length > 0) {
const request = this.offlineQueue.shift();
try {
await this.sendRequest(request);
} catch (error) {
console.error('Failed to process offline request:', error);
this.offlineQueue.unshift(request);
break;
}
}
}
showConnectionStatus(status) {
const banner = document.createElement('div');
banner.className = `connection-banner ${status}`;
banner.textContent = status === 'online' ?
'Connection restored' : 'You are offline';
document.body.appendChild(banner);
setTimeout(() => banner.remove(), 3000);
}
}
// Initialize PWA
const app = new PWAApp();
8. Web Accessibility (a11y)
Web accessibility ensures that websites and applications are usable by everyone, including people with disabilities. Implementing accessibility features improves user experience for all users and is often required by law.
8.1. WCAG Principles
The Web Content Accessibility Guidelines (WCAG) are built on four principles:
- Perceivable
-
Information must be presentable in ways users can perceive
- Operable
-
User interface components must be operable
- Understandable
-
Information and UI operation must be understandable
- Robust
-
Content must be robust enough for various assistive technologies
8.2. Semantic HTML for Accessibility
Use proper HTML elements to convey meaning:
<!-- Good: Semantic button -->
<button type="button" onclick="toggleMenu()">Menu</button>
<!-- Avoid: Div styled as button -->
<div class="button" onclick="toggleMenu()">Menu</div>
<!-- Good: Proper heading hierarchy -->
<h1>Main Title</h1>
<h2>Section Title</h2>
<h3>Subsection Title</h3>
<!-- Good: Semantic form elements -->
<form>
<fieldset>
<legend>Personal Information</legend>
<label for="name">Full Name (required)</label>
<input type="text" id="name" required aria-describedby="name-help">
<small id="name-help">Enter your first and last name</small>
<label for="email">Email</label>
<input type="email" id="email" aria-describedby="email-error">
<div id="email-error" aria-live="polite"></div>
</fieldset>
</form>
<!-- Good: Navigation structure -->
<nav aria-label="Main navigation">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
8.3. ARIA (Accessible Rich Internet Applications)
ARIA provides semantic information for complex interactive elements:
- ARIA Roles
-
Define what an element is or does
<div role="button" tabindex="0">Custom Button</div>
<div role="alert">Error message</div>
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
<h2 id="dialog-title">Confirmation</h2>
<p>Are you sure you want to delete this item?</p>
<button>Yes</button>
<button>No</button>
</div>
<ul role="tablist">
<li role="tab" aria-selected="true" aria-controls="panel1">Tab 1</li>
<li role="tab" aria-selected="false" aria-controls="panel2">Tab 2</li>
</ul>
<div id="panel1" role="tabpanel">Content 1</div>
<div id="panel2" role="tabpanel" hidden>Content 2</div>
- ARIA Properties
-
Describe element properties
<!-- Labels -->
<button aria-label="Close dialog">×</button>
<input type="password" aria-labelledby="pwd-label" aria-describedby="pwd-help">
<label id="pwd-label">Password</label>
<div id="pwd-help">Must be at least 8 characters</div>
<!-- States -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>...</ul>
<input type="checkbox" aria-checked="mixed"> <!-- Indeterminate -->
<button aria-pressed="true">Toggle Button</button>
<!-- Relationships -->
<h2 id="settings-title">Settings</h2>
<div role="region" aria-labelledby="settings-title">
<!-- Settings content -->
</div>
- ARIA Live Regions
-
Announce dynamic content changes
<!-- Polite announcements -->
<div aria-live="polite" id="status"></div>
<!-- Assertive announcements (interrupts) -->
<div aria-live="assertive" id="errors"></div>
<!-- Atomic updates -->
<div aria-live="polite" aria-atomic="true" id="timer">
Time remaining: 5:00
</div>
<script>
// Update live regions
function showStatus(message) {
document.getElementById('status').textContent = message;
}
function showError(message) {
document.getElementById('errors').textContent = message;
}
// Usage
showStatus('Data saved successfully');
showError('Please correct the highlighted errors');
</script>
8.4. Keyboard Navigation
Ensure all functionality is accessible via keyboard:
<!-- Focus management -->
<div class="modal" role="dialog" aria-labelledby="modal-title">
<h2 id="modal-title" tabindex="-1">Modal Title</h2>
<button class="close" aria-label="Close modal">×</button>
<!-- Modal content -->
<button class="confirm">Confirm</button>
</div>
<script>
class Modal {
constructor(element) {
this.element = element;
this.focusableElements = this.element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
this.firstFocusable = this.focusableElements[0];
this.lastFocusable = this.focusableElements[this.focusableElements.length - 1];
}
open() {
// Store previous focus
this.previousFocus = document.activeElement;
this.element.style.display = 'block';
// Focus title
this.element.querySelector('#modal-title').focus();
// Trap focus within modal
this.element.addEventListener('keydown', this.trapFocus.bind(this));
document.addEventListener('keydown', this.handleEscape.bind(this));
}
close() {
this.element.style.display = 'none';
// Restore previous focus
if (this.previousFocus) {
this.previousFocus.focus();
}
// Remove event listeners
this.element.removeEventListener('keydown', this.trapFocus);
document.removeEventListener('keydown', this.handleEscape);
}
trapFocus(event) {
if (event.key === 'Tab') {
if (event.shiftKey) {
// Shift + Tab
if (document.activeElement === this.firstFocusable) {
event.preventDefault();
this.lastFocusable.focus();
}
} else {
// Tab
if (document.activeElement === this.lastFocusable) {
event.preventDefault();
this.firstFocusable.focus();
}
}
}
}
handleEscape(event) {
if (event.key === 'Escape') {
this.close();
}
}
}
// Custom dropdown with keyboard support
class AccessibleDropdown {
constructor(element) {
this.element = element;
this.button = element.querySelector('[role="button"]');
this.menu = element.querySelector('[role="menu"]');
this.items = element.querySelectorAll('[role="menuitem"]');
this.currentIndex = -1;
this.setupEventListeners();
}
setupEventListeners() {
this.button.addEventListener('click', () => this.toggle());
this.button.addEventListener('keydown', (e) => this.handleButtonKeydown(e));
this.items.forEach((item, index) => {
item.addEventListener('click', () => this.selectItem(index));
item.addEventListener('keydown', (e) => this.handleItemKeydown(e, index));
});
}
handleButtonKeydown(event) {
switch (event.key) {
case 'ArrowDown':
case 'ArrowUp':
case 'Enter':
case ' ':
event.preventDefault();
this.open();
this.focusItem(0);
break;
}
}
handleItemKeydown(event, index) {
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
this.focusItem(index + 1);
break;
case 'ArrowUp':
event.preventDefault();
this.focusItem(index - 1);
break;
case 'Home':
event.preventDefault();
this.focusItem(0);
break;
case 'End':
event.preventDefault();
this.focusItem(this.items.length - 1);
break;
case 'Enter':
case ' ':
event.preventDefault();
this.selectItem(index);
break;
case 'Escape':
event.preventDefault();
this.close();
this.button.focus();
break;
}
}
}
</script>
8.5. Color and Contrast
Ensure sufficient color contrast and don’t rely solely on color:
/* Good contrast ratios */
.text-normal {
color: #333333; /* 12.63:1 ratio on white */
background-color: #ffffff;
}
.text-large {
color: #666666; /* 5.74:1 ratio - acceptable for large text */
background-color: #ffffff;
font-size: 18px;
font-weight: bold;
}
/* Error states - don't rely only on color */
.error {
color: #d32f2f;
border: 2px solid #d32f2f;
}
.error::before {
content: "⚠ ";
font-weight: bold;
}
/* Focus indicators */
.focusable:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
/* High contrast mode support */
@media (prefers-contrast: high) {
.card {
border: 2px solid;
}
}
/* Respect user's motion preferences */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Color blind friendly palette */
:root {
--accessible-blue: #005fcc;
--accessible-orange: #ff6f00;
--accessible-green: #00c853;
--accessible-red: #d32f2f;
}
8.6. Images and Media
Provide alternative text and captions:
<!-- Informative images -->
<img src="chart.png" alt="Sales increased 25% from 2023 to 2024">
<!-- Decorative images -->
<img src="decoration.png" alt="" role="presentation">
<!-- Complex images -->
<img src="complex-chart.png" alt="Quarterly sales data" aria-describedby="chart-description">
<div id="chart-description">
<p>The chart shows quarterly sales for 2024:</p>
<ul>
<li>Q1: $100,000</li>
<li>Q2: $150,000</li>
<li>Q3: $125,000</li>
<li>Q4: $175,000</li>
</ul>
</div>
<!-- Videos with captions -->
<video controls>
<source src="video.mp4" type="video/mp4">
<track kind="subtitles" src="subtitles-en.vtt" srclang="en" label="English">
<track kind="captions" src="captions-en.vtt" srclang="en" label="English Captions">
<p>Your browser doesn't support HTML5 video.
<a href="video.mp4">Download the video</a>.</p>
</video>
<!-- Audio with transcripts -->
<audio controls>
<source src="podcast.mp3" type="audio/mpeg">
<p>Your browser doesn't support HTML5 audio.</p>
</audio>
<details>
<summary>Transcript</summary>
<p>Full transcript of the audio content...</p>
</details>
8.7. Accessible Forms
Create forms that work well with assistive technologies:
<form novalidate aria-labelledby="form-title">
<h2 id="form-title">Contact Form</h2>
<fieldset>
<legend>Personal Information</legend>
<div class="form-group">
<label for="firstName">First Name *</label>
<input
type="text"
id="firstName"
name="firstName"
required
aria-invalid="false"
aria-describedby="firstName-error"
>
<div id="firstName-error" class="error-message" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="email">Email Address *</label>
<input
type="email"
id="email"
name="email"
required
autocomplete="email"
aria-describedby="email-help email-error"
>
<div id="email-help" class="help-text">We'll never share your email</div>
<div id="email-error" class="error-message" aria-live="polite"></div>
</div>
<fieldset>
<legend>Preferred Contact Method</legend>
<div class="radio-group">
<input type="radio" id="contact-email" name="contactMethod" value="email">
<label for="contact-email">Email</label>
</div>
<div class="radio-group">
<input type="radio" id="contact-phone" name="contactMethod" value="phone">
<label for="contact-phone">Phone</label>
</div>
</fieldset>
<div class="form-group">
<label for="message">Message *</label>
<textarea
id="message"
name="message"
required
maxlength="500"
aria-describedby="message-count message-error"
></textarea>
<div id="message-count" aria-live="polite">0/500 characters</div>
<div id="message-error" class="error-message" aria-live="polite"></div>
</div>
</fieldset>
<div class="form-actions">
<button type="submit">Send Message</button>
<button type="reset">Clear Form</button>
</div>
<div id="form-status" aria-live="polite" aria-atomic="true"></div>
</form>
<script>
// Accessible form validation
class AccessibleForm {
constructor(form) {
this.form = form;
this.setupValidation();
}
setupValidation() {
this.form.addEventListener('submit', (e) => this.handleSubmit(e));
// Real-time validation for better UX
this.form.querySelectorAll('input, textarea').forEach(field => {
field.addEventListener('blur', () => this.validateField(field));
field.addEventListener('input', () => this.clearError(field));
});
}
validateField(field) {
const isValid = field.checkValidity();
const errorElement = document.getElementById(`${field.id}-error`);
field.setAttribute('aria-invalid', !isValid);
if (!isValid) {
const errorMessage = this.getErrorMessage(field);
errorElement.textContent = errorMessage;
} else {
errorElement.textContent = '';
}
}
getErrorMessage(field) {
if (field.validity.valueMissing) {
return `${this.getFieldName(field)} is required.`;
}
if (field.validity.typeMismatch) {
return `Please enter a valid ${field.type}.`;
}
if (field.validity.tooLong) {
return `${this.getFieldName(field)} is too long.`;
}
return 'Please correct this field.';
}
getFieldName(field) {
const label = this.form.querySelector(`label[for="${field.id}"]`);
return label ? label.textContent.replace(' *', '') : field.name;
}
}
// Character counter
function updateCharacterCount(textarea) {
const counter = document.getElementById(`${textarea.id}-count`);
const current = textarea.value.length;
const max = textarea.maxLength;
counter.textContent = `${current}/${max} characters`;
if (current > max * 0.9) {
counter.classList.add('warning');
} else {
counter.classList.remove('warning');
}
}
</script>
8.8. Accessibility Testing
- Automated Testing
-
Use tools like axe-core for automated testing
// Install: npm install @axe-core/react
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('should not have accessibility violations', async () => {
const { container } = render(<MyComponent />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
// Manual testing with axe in browser
if (process.env.NODE_ENV === 'development') {
import('@axe-core/react').then(axe => {
axe.default(React, ReactDOM, 1000);
});
}
- Manual Testing Checklist
-
-
Navigate entire site using only keyboard
-
Test with screen reader (NVDA, JAWS, VoiceOver)
-
Verify color contrast meets WCAG standards
-
Test with 200% zoom
-
Verify all images have appropriate alt text
-
Check form labels and error messages
-
Test focus indicators are visible
-
Verify heading hierarchy is logical
-
8.9. Accessibility Resources
- Guidelines and Standards
-
-
WCAG 2.1 AA compliance
-
Section 508 (US government)
-
EN 301 549 (European standard)
-
ADA compliance
-
- Testing Tools
-
-
axe DevTools browser extension
-
WAVE Web Accessibility Evaluator
-
Lighthouse accessibility audit
-
Color contrast analyzers
-
Screen readers (NVDA, JAWS, VoiceOver, Talkback)
-
9. Web Performance Optimization
Web performance directly impacts user experience, conversion rates, and search engine rankings. Modern web applications must be optimized for speed across all devices and network conditions.
9.1. Core Web Vitals
Google’s Core Web Vitals measure user experience quality:
- Largest Contentful Paint (LCP)
-
Loading performance - should occur within 2.5 seconds
- First Input Delay (FID)
-
Interactivity - should be less than 100 milliseconds
- Cumulative Layout Shift (CLS)
-
Visual stability - should be less than 0.1
- First Contentful Paint (FCP)
-
Time when first content appears - should be under 1.8 seconds
- Time to Interactive (TTI)
-
When page becomes fully interactive
9.2. Loading Optimization
- Resource Prioritization
-
Load critical resources first
<!DOCTYPE html>
<html>
<head>
<!-- Critical CSS inline -->
<style>
/* Above-the-fold styles */
body { font-family: system-ui; margin: 0; }
.header { background: #333; color: white; padding: 1rem; }
</style>
<!-- Preload critical resources -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/css/critical.css" as="style">
<link rel="preload" href="/js/main.js" as="script">
<!-- DNS prefetch for external resources -->
<link rel="dns-prefetch" href="//fonts.googleapis.com">
<link rel="dns-prefetch" href="//api.example.com">
<!-- Preconnect to critical origins -->
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Load non-critical CSS asynchronously -->
<link rel="preload" href="/css/non-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/css/non-critical.css"></noscript>
</head>
<body>
<!-- Critical above-the-fold content -->
<header class="header">
<h1>My Website</h1>
</header>
<!-- Lazy load non-critical images -->
<img src="placeholder.jpg" data-src="hero-image.jpg" class="lazy" alt="Hero image">
<!-- Load JavaScript at the end -->
<script src="/js/main.js" defer></script>
</body>
</html>
- Code Splitting
-
Split code into smaller chunks
// Dynamic imports for code splitting
const loadFeature = async () => {
const { AdvancedFeature } = await import('./advanced-feature.js');
return new AdvancedFeature();
};
// Route-based code splitting
const router = {
async loadRoute(routeName) {
switch (routeName) {
case 'home':
const { HomePage } = await import('./pages/home.js');
return new HomePage();
case 'dashboard':
const { DashboardPage } = await import('./pages/dashboard.js');
return new DashboardPage();
default:
const { NotFoundPage } = await import('./pages/404.js');
return new NotFoundPage();
}
}
};
// Conditional loading based on device capabilities
if ('IntersectionObserver' in window) {
import('./lazy-loading.js').then(module => {
module.initializeLazyLoading();
});
} else {
// Fallback for older browsers
import('./legacy-loading.js');
}
// Feature-based loading
const loadAnalytics = () => {
if (user.hasConsented) {
import('./analytics.js').then(analytics => {
analytics.initialize();
});
}
};
9.3. Image Optimization
- Modern Image Formats
-
Use WebP and AVIF when supported
<picture>
<!-- AVIF for modern browsers -->
<source srcset="image.avif" type="image/avif">
<!-- WebP for browsers that support it -->
<source srcset="image.webp" type="image/webp">
<!-- Fallback JPEG -->
<img src="image.jpg" alt="Description" loading="lazy">
</picture>
<!-- Responsive images with srcset -->
<img
src="image-400.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w,
image-1600.jpg 1600w
"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
alt="Responsive image"
loading="lazy"
>
- Lazy Loading Implementation
class LazyImageLoader {
constructor(options = {}) {
this.options = {
threshold: 0.1,
rootMargin: '50px',
...options
};
this.observer = new IntersectionObserver(
this.handleIntersection.bind(this),
this.options
);
this.init();
}
init() {
const lazyImages = document.querySelectorAll('img[data-src]');
lazyImages.forEach(img => this.observer.observe(img));
}
handleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadImage(entry.target);
this.observer.unobserve(entry.target);
}
});
}
loadImage(img) {
// Create new image to preload
const imageLoader = new Image();
imageLoader.onload = () => {
// Replace placeholder with actual image
img.src = img.dataset.src;
img.classList.remove('lazy');
img.classList.add('loaded');
// Handle srcset for responsive images
if (img.dataset.srcset) {
img.srcset = img.dataset.srcset;
}
};
imageLoader.onerror = () => {
img.classList.add('error');
};
imageLoader.src = img.dataset.src;
}
}
// Initialize lazy loading
new LazyImageLoader();
// Progressive image loading with blur effect
class ProgressiveImageLoader {
constructor(container) {
this.container = container;
this.lowQualitySrc = container.dataset.lowQuality;
this.highQualitySrc = container.dataset.highQuality;
this.load();
}
load() {
// Load low quality image first
const lowQualityImg = new Image();
lowQualityImg.onload = () => {
this.container.style.backgroundImage = `url(${this.lowQualitySrc})`;
this.container.classList.add('low-quality-loaded');
// Start loading high quality image
this.loadHighQuality();
};
lowQualityImg.src = this.lowQualitySrc;
}
loadHighQuality() {
const highQualityImg = new Image();
highQualityImg.onload = () => {
this.container.style.backgroundImage = `url(${this.highQualitySrc})`;
this.container.classList.remove('low-quality-loaded');
this.container.classList.add('high-quality-loaded');
};
highQualityImg.src = this.highQualitySrc;
}
}
9.4. JavaScript Optimization
- Efficient DOM Manipulation
-
Minimize reflows and repaints
// Bad: Multiple DOM operations
function badUpdate(items) {
const container = document.getElementById('container');
// This causes multiple reflows
items.forEach(item => {
const element = document.createElement('div');
element.textContent = item.name;
element.style.color = item.color; // Causes repaint
container.appendChild(element); // Causes reflow
});
}
// Good: Batch DOM operations
function goodUpdate(items) {
const fragment = document.createDocumentFragment();
items.forEach(item => {
const element = document.createElement('div');
element.textContent = item.name;
element.className = item.cssClass; // Use CSS classes instead
fragment.appendChild(element);
});
// Single DOM operation
document.getElementById('container').appendChild(fragment);
}
// Use CSS transforms for animations (GPU accelerated)
function animateElement(element, x, y) {
// Bad: Changes layout
// element.style.left = x + 'px';
// element.style.top = y + 'px';
// Good: Uses compositor
element.style.transform = `translate3d(${x}px, ${y}px, 0)`;
}
// Virtual scrolling for large lists
class VirtualScroller {
constructor(container, items, itemHeight) {
this.container = container;
this.items = items;
this.itemHeight = itemHeight;
this.visibleCount = Math.ceil(container.clientHeight / itemHeight) + 2;
this.scrollTop = 0;
this.startIndex = 0;
this.setup();
}
setup() {
// Create scrollable area
this.scrollContainer = document.createElement('div');
this.scrollContainer.style.height = this.items.length * this.itemHeight + 'px';
// Create viewport
this.viewport = document.createElement('div');
this.viewport.style.height = '100%';
this.viewport.style.overflow = 'auto';
this.container.appendChild(this.viewport);
this.viewport.appendChild(this.scrollContainer);
// Handle scroll events
this.viewport.addEventListener('scroll', () => {
this.handleScroll();
});
this.render();
}
handleScroll() {
const scrollTop = this.viewport.scrollTop;
const newStartIndex = Math.floor(scrollTop / this.itemHeight);
if (newStartIndex !== this.startIndex) {
this.startIndex = newStartIndex;
this.render();
}
}
render() {
// Clear existing items
const existingItems = this.scrollContainer.querySelectorAll('.virtual-item');
existingItems.forEach(item => item.remove());
// Render visible items
const endIndex = Math.min(
this.startIndex + this.visibleCount,
this.items.length
);
for (let i = this.startIndex; i < endIndex; i++) {
const item = this.createItem(this.items[i], i);
item.style.position = 'absolute';
item.style.top = i * this.itemHeight + 'px';
item.style.height = this.itemHeight + 'px';
item.className = 'virtual-item';
this.scrollContainer.appendChild(item);
}
}
createItem(data, index) {
const item = document.createElement('div');
item.textContent = `Item ${index}: ${data.name}`;
return item;
}
}
9.5. Caching Strategies
- HTTP Caching
-
Configure proper cache headers
# Static assets - cache for 1 year
/static/*
Cache-Control: public, max-age=31536000, immutable
# HTML files - validate with server
/*.html
Cache-Control: public, max-age=0, must-revalidate
# API responses - cache for 5 minutes
/api/*
Cache-Control: public, max-age=300
# User-specific content - don't cache
/dashboard/*
Cache-Control: private, no-cache, no-store, must-revalidate
- Service Worker Caching
// Advanced caching strategies
const CACHE_NAME = 'app-cache-v1';
const RUNTIME_CACHE = 'runtime-cache';
class CacheManager {
constructor() {
this.strategies = {
cacheFirst: this.cacheFirst.bind(this),
networkFirst: this.networkFirst.bind(this),
staleWhileRevalidate: this.staleWhileRevalidate.bind(this)
};
}
async cacheFirst(request) {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(request);
if (cached) {
return cached;
}
try {
const response = await fetch(request);
cache.put(request, response.clone());
return response;
} catch (error) {
// Return offline fallback if available
return await cache.match('/offline.html') ||
new Response('Offline', { status: 503 });
}
}
async networkFirst(request) {
try {
const response = await fetch(request);
const cache = await caches.open(RUNTIME_CACHE);
cache.put(request, response.clone());
return response;
} catch (error) {
const cache = await caches.open(RUNTIME_CACHE);
const cached = await cache.match(request);
return cached || new Response('Network Error', { status: 503 });
}
}
async staleWhileRevalidate(request) {
const cache = await caches.open(RUNTIME_CACHE);
const cached = await cache.match(request);
// Start network request in background
const networkRequest = fetch(request).then(response => {
cache.put(request, response.clone());
return response;
});
// Return cached version immediately if available
return cached || networkRequest;
}
}
// Memory caching for API responses
class MemoryCache {
constructor(maxSize = 100, ttl = 300000) { // 5 minutes TTL
this.cache = new Map();
this.maxSize = maxSize;
this.ttl = ttl;
}
set(key, value) {
if (this.cache.size >= this.maxSize) {
// Remove oldest entry
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, {
value,
timestamp: Date.now()
});
}
get(key) {
const entry = this.cache.get(key);
if (!entry) {
return null;
}
// Check if expired
if (Date.now() - entry.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return entry.value;
}
clear() {
this.cache.clear();
}
}
// Usage
const apiCache = new MemoryCache();
async function cachedFetch(url, options = {}) {
const cacheKey = url + JSON.stringify(options);
// Check memory cache first
const cached = apiCache.get(cacheKey);
if (cached) {
return Promise.resolve(new Response(JSON.stringify(cached)));
}
// Fetch from network
const response = await fetch(url, options);
const data = await response.json();
// Cache the response
apiCache.set(cacheKey, data);
return new Response(JSON.stringify(data));
}
9.6. Performance Monitoring
- Web Performance APIs
// Performance monitoring
class PerformanceMonitor {
constructor() {
this.metrics = {};
this.init();
}
init() {
// Core Web Vitals
this.measureCWV();
// Navigation timing
this.measureNavigation();
// Resource timing
this.measureResources();
// Custom metrics
this.measureCustom();
}
measureCWV() {
// Largest Contentful Paint
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
this.metrics.lcp = lastEntry.startTime;
this.sendMetric('lcp', lastEntry.startTime);
}).observe({ type: 'largest-contentful-paint', buffered: true });
// First Input Delay
new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
this.metrics.fid = entry.processingStart - entry.startTime;
this.sendMetric('fid', entry.processingStart - entry.startTime);
});
}).observe({ type: 'first-input', buffered: true });
// Cumulative Layout Shift
let clsScore = 0;
new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
if (!entry.hadRecentInput) {
clsScore += entry.value;
}
});
this.metrics.cls = clsScore;
this.sendMetric('cls', clsScore);
}).observe({ type: 'layout-shift', buffered: true });
}
measureNavigation() {
const navigation = performance.getEntriesByType('navigation')[0];
this.metrics.ttfb = navigation.responseStart - navigation.requestStart;
this.metrics.domLoad = navigation.domContentLoadedEventEnd - navigation.navigationStart;
this.metrics.windowLoad = navigation.loadEventEnd - navigation.navigationStart;
this.sendMetrics({
ttfb: this.metrics.ttfb,
domLoad: this.metrics.domLoad,
windowLoad: this.metrics.windowLoad
});
}
measureResources() {
const resources = performance.getEntriesByType('resource');
resources.forEach(resource => {
if (resource.name.includes('.js') || resource.name.includes('.css')) {
this.sendMetric('resource_load', {
name: resource.name,
duration: resource.duration,
size: resource.transferSize
});
}
});
}
measureCustom() {
// Time to Interactive (custom implementation)
this.measureTimeToInteractive();
// Bundle size tracking
this.trackBundleSize();
}
measureTimeToInteractive() {
let tti = 0;
// Simplified TTI calculation
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const longTasks = entries.filter(entry => entry.duration > 50);
if (longTasks.length === 0) {
tti = performance.now();
this.sendMetric('tti', tti);
observer.disconnect();
}
});
observer.observe({ type: 'longtask', buffered: true });
}
trackBundleSize() {
const scripts = document.querySelectorAll('script[src]');
let totalSize = 0;
scripts.forEach(script => {
const resource = performance.getEntriesByName(script.src)[0];
if (resource) {
totalSize += resource.transferSize;
}
});
this.sendMetric('bundle_size', totalSize);
}
sendMetric(name, value) {
// Send to analytics service
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/metrics', JSON.stringify({
metric: name,
value: value,
timestamp: Date.now(),
url: location.href
}));
}
}
sendMetrics(metrics) {
Object.entries(metrics).forEach(([name, value]) => {
this.sendMetric(name, value);
});
}
}
// Initialize performance monitoring
new PerformanceMonitor();
// Real User Monitoring (RUM)
class RUMCollector {
constructor() {
this.data = {
connection: this.getConnectionInfo(),
device: this.getDeviceInfo(),
timing: {},
errors: []
};
this.collectErrors();
}
getConnectionInfo() {
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
return {
effectiveType: connection?.effectiveType,
downlink: connection?.downlink,
rtt: connection?.rtt,
saveData: connection?.saveData
};
}
getDeviceInfo() {
return {
userAgent: navigator.userAgent,
memory: navigator.deviceMemory,
cores: navigator.hardwareConcurrency,
platform: navigator.platform,
viewport: {
width: window.innerWidth,
height: window.innerHeight
}
};
}
collectErrors() {
window.addEventListener('error', (event) => {
this.data.errors.push({
message: event.message,
filename: event.filename,
line: event.lineno,
column: event.colno,
timestamp: Date.now()
});
});
window.addEventListener('unhandledrejection', (event) => {
this.data.errors.push({
message: event.reason?.message || 'Unhandled Promise Rejection',
timestamp: Date.now()
});
});
}
collect() {
return {
...this.data,
timing: this.getTimingData()
};
}
getTimingData() {
const navigation = performance.getEntriesByType('navigation')[0];
const paint = performance.getEntriesByType('paint');
return {
navigation: {
redirectTime: navigation.redirectEnd - navigation.redirectStart,
dnsTime: navigation.domainLookupEnd - navigation.domainLookupStart,
connectTime: navigation.connectEnd - navigation.connectStart,
responseTime: navigation.responseEnd - navigation.responseStart,
domTime: navigation.domContentLoadedEventEnd - navigation.navigationStart,
loadTime: navigation.loadEventEnd - navigation.navigationStart
},
paint: {
fcp: paint.find(entry => entry.name === 'first-contentful-paint')?.startTime,
fp: paint.find(entry => entry.name === 'first-paint')?.startTime
}
};
}
}
9.7. Performance Testing Tools
- Lighthouse
-
Automated auditing for performance, accessibility, and best practices
- WebPageTest
-
Detailed performance analysis with waterfall charts
- Chrome DevTools
-
Performance profiling and debugging
- Real User Monitoring
-
Track actual user experience metrics
- Synthetic Monitoring
-
Automated performance testing from multiple locations
Key optimization strategies: - Minimize HTTP requests - Optimize images and use modern formats - Implement efficient caching strategies - Use CDNs for global content delivery - Minimize and compress JavaScript/CSS - Implement lazy loading for non-critical resources - Optimize database queries and server response times - Use performance monitoring to track improvements
10. Links and Literature
10.11. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting