4. Chapter 4: HTML for Web Mapping#

4.1. Learning Objectives#

By the end of this chapter, you will understand:

  • Advanced HTML structure patterns for complex Web GIS applications

  • Semantic HTML elements that enhance accessibility and SEO for mapping interfaces

  • Best practices for embedding and organizing map containers in different layouts

  • Comprehensive accessibility considerations for geospatial applications

  • Progressive enhancement strategies for mapping applications

  • HTML5 APIs relevant to Web GIS development

  • Cross-browser compatibility considerations for mapping applications

  • Performance optimization through proper HTML structure

4.2. HTML Fundamentals for Web Mapping#

HTML (HyperText Markup Language) provides the structural foundation for all web applications, including Web GIS. While modern mapping libraries handle much of the complexity, understanding proper HTML structure is essential for creating accessible, maintainable, and SEO-friendly geospatial applications.

4.2.1. Basic HTML Document Structure#

Every Web GIS application starts with a well-structured HTML document:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Web GIS App</title>
    <link href="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.css" rel="stylesheet">
</head>
<body>
    <header>
        <h1>My Map Application</h1>
    </header>
    
    <main>
        <div id="map" role="application" aria-label="Interactive map"></div>
        <aside id="controls">
            <h2>Map Controls</h2>
            <!-- Controls go here -->
        </aside>
    </main>
    
    <script src="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.js"></script>
    <script src="app.js"></script>
</body>
</html>

4.2.2. Essential Meta Tags for Web GIS#

Meta tags provide crucial information for browsers and search engines:

<head>
    <!-- Essential meta tags -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Interactive map application">
    
    <!-- Social media sharing -->
    <meta property="og:title" content="My Map App">
    <meta property="og:description" content="Find locations on our interactive map">
    <meta property="og:image" content="map-preview.jpg">
</head>

4.3. Semantic Elements for Mapping Interfaces#

Semantic HTML elements improve accessibility, SEO, and code maintainability in Web GIS applications.

4.3.1. Document Structure Elements#

<body>
    <nav>
        <ul>
            <li><a href="#map">Map</a></li>
            <li><a href="#data">Data</a></li>
            <li><a href="#about">About</a></li>
        </ul>
    </nav>
    
    <header>
        <h1>Environmental Dashboard</h1>
    </header>
    
    <main>
        <div id="map" role="application" aria-label="Interactive map"></div>
        
        <aside id="controls">
            <h2>Layers</h2>
            <label>
                <input type="checkbox" name="layer" value="air-quality" checked>
                Air Quality
            </label>
            <label>
                <input type="checkbox" name="layer" value="weather">
                Weather
            </label>
        </aside>
    </main>
    
    <footer>
        <p>&copy; 2024 Environmental Portal</p>
    </footer>
</body>

4.3.2. Interactive Elements#

Web GIS applications require various interactive elements:

<!-- Search box -->
<section id="search">
    <label for="location-input">Search location:</label>
    <input type="search" id="location-input" placeholder="Enter address">
    <button type="submit">Search</button>
</section>

<!-- Simple filters -->
<section id="filters">
    <h2>Filters</h2>
    
    <label for="start-date">From:</label>
    <input type="date" id="start-date">
    
    <label for="end-date">To:</label>
    <input type="date" id="end-date">
    
    <select id="category-select">
        <option value="restaurants">Restaurants</option>
        <option value="shops">Shops</option>
        <option value="services">Services</option>
    </select>
    
    <button id="apply-filters">Apply</button>
</section>

4.4. Structuring a Map Page#

4.4.1. Single-Page Map Application#

For simple mapping applications, organize content logically:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>City Parks Map</title>
    <link href="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.css" rel="stylesheet">
    <style>
        body { margin: 0; font-family: system-ui, sans-serif; }
        
        .app-container {
            height: 100vh;
            display: grid;
            grid-template-areas: "header header" "sidebar map";
            grid-template-rows: auto 1fr;
            grid-template-columns: 300px 1fr;
        }
        
        .app-header { grid-area: header; background: #2c3e50; color: white; padding: 1rem; }
        .app-sidebar { grid-area: sidebar; background: #ecf0f1; padding: 1rem; }
        .map-container { grid-area: map; }
        
        @media (max-width: 768px) {
            .app-container {
                grid-template-areas: "header" "map" "sidebar";
                grid-template-columns: 1fr;
                grid-template-rows: auto 50vh auto;
            }
        }
    </style>
</head>
<body>
    <div class="app-container">
        <header class="app-header">
            <h1>City Parks & Recreation</h1>
        </header>
        
        <aside class="app-sidebar">
            <h2>Map Layers</h2>
            <label><input type="checkbox" name="layer" value="parks" checked> Parks</label>
            <label><input type="checkbox" name="layer" value="trails"> Trails</label>
            <label><input type="checkbox" name="layer" value="facilities"> Facilities</label>
        </aside>
        
        <main class="map-container">
            <div id="map" 
                 role="application" 
                 aria-label="Interactive map of city parks"
                 style="width: 100%; height: 100%;">
            </div>
        </main>
    </div>
    
    <script src="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.js"></script>
    <script src="app.js"></script>
</body>
</html>

4.4.2. Multi-Page Application Structure#

For larger applications, organize content across multiple pages:

<!-- index.html - Main map page -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GIS Portal - Map View</title>
    <link href="styles/main.css" rel="stylesheet">
</head>
<body>
    <nav class="main-navigation">
        <ul>
            <li><a href="index.html" aria-current="page">Map</a></li>
            <li><a href="data.html">Data Browser</a></li>
            <li><a href="analysis.html">Analysis Tools</a></li>
            <li><a href="help.html">Help</a></li>
        </ul>
    </nav>
    
    <main>
        <div id="map" role="application" aria-label="Main GIS map"></div>
    </main>
    
    <script type="module" src="js/map.js"></script>
</body>
</html>
<!-- Additional pages follow the same simple pattern -->
<!DOCTYPE html>
<html>
<head>
    <title>GIS Portal - Data</title>
</head>
<body>
    <nav>
        <a href="index.html">Map</a>
        <a href="data.html">Data</a>
    </nav>
    <main>
        <h1>Data Browser</h1>
        <div id="dataset-catalog"></div>
    </main>
</body>
</html>

4.5. Embedding Map Containers#

4.5.1. Basic Map Container#

The map container is the foundation of any Web GIS application:

<!-- Basic map container -->
<div id="map" style="width: 100%; height: 400px;">
    <div class="loading">Loading map...</div>
</div>

4.5.2. Responsive Map Container#

Create maps that adapt to different screen sizes:

<!-- Responsive map with aspect ratio -->
<div class="map-wrapper">
    <div id="map"></div>
</div>
.map-wrapper {
    position: relative;
    width: 100%;
    padding-bottom: 56.25%; /* 16:9 ratio */
}

#map {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

4.5.3. Map with Overlays#

Add UI elements on top of the map:

<!-- Map with simple overlays -->
<div class="map-wrapper">
    <div id="map"></div>
    
    <div class="controls top-left">
        <h3>Layers</h3>
        <label>
            <input type="checkbox" checked> Population
        </label>
        <label>
            <input type="checkbox"> Transportation
        </label>
    </div>
    
    <div class="controls top-right">
        <input type="search" placeholder="Search...">
    </div>
    
    <div class="controls bottom-right">
        <p>© Map contributors</p>
    </div>
</div>
.map-wrapper {
    position: relative;
    height: 500px;
}

.controls {
    position: absolute;
    background: rgba(255, 255, 255, 0.9);
    padding: 10px;
    border-radius: 4px;
    z-index: 1000;
}

.top-left { top: 10px; left: 10px; }
.top-right { top: 10px; right: 10px; }
.bottom-right { bottom: 10px; right: 10px; }

4.6. Accessibility Considerations#

Web GIS applications must be accessible to users with disabilities.

4.6.1. ARIA Labels and Roles#

Use ARIA attributes to describe map functionality:

<!-- Map container with proper ARIA -->
<div 
    id="map"
    role="application"
    aria-label="Interactive map showing local businesses"
    aria-describedby="map-instructions"
    tabindex="0"
>
    <div id="map-instructions" class="visually-hidden">
        Use arrow keys to pan the map, plus and minus keys to zoom.
        Press Tab to navigate to map controls.
    </div>
</div>

<!-- Map controls with ARIA -->
<div class="map-controls" role="toolbar" aria-label="Map controls">
    <button 
        type="button" 
        id="zoom-in"
        aria-label="Zoom in"
        title="Zoom in"
    >
        <span aria-hidden="true">+</span>
    </button>
    
    <button 
        type="button" 
        id="zoom-out"
        aria-label="Zoom out" 
        title="Zoom out"
    >
        <span aria-hidden="true">-</span>
    </button>
    
    <button 
        type="button" 
        id="fullscreen"
        aria-label="Toggle fullscreen"
        aria-pressed="false"
    >
        <span aria-hidden="true"></span>
    </button>
</div>

4.6.2. Keyboard Navigation#

Enable keyboard interaction for map controls:

<div class="layer-toggle-group" role="group" aria-labelledby="layer-heading">
    <h3 id="layer-heading">Map Layers</h3>
    
    <fieldset>
        <legend class="visually-hidden">Select layers to display</legend>
        
        <label class="layer-control">
            <input 
                type="checkbox" 
                name="layer" 
                value="restaurants"
                aria-describedby="restaurants-desc"
            >
            <span>Restaurants</span>
            <span id="restaurants-desc" class="layer-description">
                Shows restaurant locations and ratings
            </span>
        </label>
        
        <label class="layer-control">
            <input 
                type="checkbox" 
                name="layer" 
                value="transit"
                aria-describedby="transit-desc"
            >
            <span>Public Transit</span>
            <span id="transit-desc" class="layer-description">
                Shows bus stops and train stations
            </span>
        </label>
    </fieldset>
</div>

4.6.3. Screen Reader Support#

Provide alternative content for screen readers:

<!-- Accessible map with alternative content -->
<div class="map-container">
    <div id="map" role="application" aria-label="Business locations"></div>
    
    <div class="sr-only">
        <h2>Business List</h2>
        <p>Showing <span id="count">0</span> businesses</p>
        <table id="business-table">
            <tr><th>Name</th><th>Address</th></tr>
            <!-- Content filled by JavaScript -->
        </table>
    </div>
</div>

<style>
.sr-only {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
}
</style>

4.6.4. Focus Management#

Ensure proper focus management for interactive elements:

<!-- Simple accessible popup -->
<div class="popup" role="dialog">
    <h2>Location Details</h2>
    <button class="close" aria-label="Close">&times;</button>
    
    <p><strong>Address:</strong> <span id="address"></span></p>
    <p><strong>Hours:</strong> <span id="hours"></span></p>
    
    <button>Get Directions</button>
    <button>More Info</button>
</div>

4.7. Advanced HTML Patterns for Web GIS#

4.7.1. Progressive Enhancement Strategy#

Build applications that work for all users, then enhance with JavaScript:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Progressive Enhancement Map</title>
    <style>
        .no-js .map-fallback { display: block; }
        .js .map-fallback { display: none; }
        .no-js .map-container { display: none; }
        .js .map-container { display: block; }
    </style>
</head>
<body class="no-js">
    <!-- Fallback content for users without JavaScript -->
    <div class="map-fallback">
        <h1>Store Locations</h1>
        <p>Our stores are located in the following areas:</p>
        <ul>
            <li>Downtown - 123 Main St, Phone: (555) 123-4567</li>
            <li>Mall Location - 456 Shopping Blvd, Phone: (555) 987-6543</li>
            <li>Airport - Terminal 2, Gate B5, Phone: (555) 555-0123</li>
        </ul>
        <p><a href="directions.html">Get detailed directions</a></p>
    </div>
    
    <!-- Enhanced interactive map -->
    <div class="map-container">
        <div id="map" role="application" aria-label="Interactive store locator map"></div>
        <div class="map-controls">
            <button id="find-nearest">Find Nearest Store</button>
        </div>
    </div>
    
    <script>
        // Remove no-js class to enable enhanced experience
        document.documentElement.className = document.documentElement.className.replace('no-js', 'js');
        
        // Initialize map functionality
        import('./js/map-initialization.js');
    </script>
</body>
</html>

4.7.2. Complex Application Layout Patterns#

Dashboard Layout with Multiple Maps:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GIS Dashboard</title>
    <style>
        .dashboard {
            display: grid;
            grid-template-areas: 
                "header header header"
                "sidebar main-map overview-map"
                "sidebar data-panel overview-map";
            grid-template-columns: 250px 1fr 300px;
            grid-template-rows: 60px 1fr 200px;
            height: 100vh;
            gap: 8px;
            padding: 8px;
        }
        
        .header { grid-area: header; }
        .sidebar { grid-area: sidebar; }
        .main-map { grid-area: main-map; }
        .overview-map { grid-area: overview-map; }
        .data-panel { grid-area: data-panel; }
        
        @media (max-width: 1024px) {
            .dashboard {
                grid-template-areas: 
                    "header"
                    "main-map"
                    "sidebar"
                    "data-panel";
                grid-template-columns: 1fr;
                grid-template-rows: 60px 50vh auto auto;
            }
            .overview-map { display: none; }
        }
    </style>
</head>
<body>
    <div class="dashboard">
        <header class="header">
            <h1>Environmental Monitoring Dashboard</h1>
            <nav>
                <button aria-pressed="false" data-view="air-quality">Air Quality</button>
                <button aria-pressed="false" data-view="water-quality">Water Quality</button>
                <button aria-pressed="false" data-view="noise-levels">Noise Levels</button>
            </nav>
        </header>
        
        <aside class="sidebar">
            <h2>Monitoring Stations</h2>
            <div class="station-list" role="list">
                <div class="station-item" role="listitem" data-station="001">
                    <h3>Downtown Station</h3>
                    <p class="status good">Status: Good</p>
                    <p class="last-update">Updated: 5 min ago</p>
                </div>
            </div>
        </aside>
        
        <main class="main-map">
            <div id="primary-map" 
                 role="application" 
                 aria-label="Primary monitoring map"
                 aria-describedby="map-status">
            </div>
            <div id="map-status" class="visually-hidden">
                Showing data from 15 monitoring stations
            </div>
        </main>
        
        <div class="overview-map">
            <h3>Regional Overview</h3>
            <div id="overview-map" 
                 role="img" 
                 aria-label="Regional overview of monitoring data">
            </div>
        </div>
        
        <section class="data-panel">
            <h2>Real-time Data</h2>
            <table class="data-table">
                <caption>Current sensor readings</caption>
                <thead>
                    <tr>
                        <th scope="col">Parameter</th>
                        <th scope="col">Value</th>
                        <th scope="col">Trend</th>
                    </tr>
                </thead>
                <tbody id="sensor-data">
                    <!-- Data populated by JavaScript -->
                </tbody>
            </table>
        </section>
    </div>
</body>
</html>

4.7.3. HTML5 APIs for Web GIS#

Geolocation Integration:

<section class="location-services">
    <h2>Location Services</h2>
    <button id="get-location" type="button">
        <span class="icon" aria-hidden="true">📍</span>
        Find My Location
    </button>
    
    <div id="location-status" role="status" aria-live="polite" class="visually-hidden">
        <!-- Status updates appear here -->
    </div>
    
    <div id="location-error" role="alert" class="error-message" style="display: none;">
        <!-- Error messages appear here -->
    </div>
</section>

<script>
document.getElementById('get-location').addEventListener('click', () => {
    const statusEl = document.getElementById('location-status');
    const errorEl = document.getElementById('location-error');
    
    if (!navigator.geolocation) {
        errorEl.textContent = 'Geolocation is not supported by this browser.';
        errorEl.style.display = 'block';
        return;
    }
    
    statusEl.textContent = 'Getting your location...';
    
    navigator.geolocation.getCurrentPosition(
        (position) => {
            statusEl.textContent = `Location found: ${position.coords.latitude.toFixed(4)}, ${position.coords.longitude.toFixed(4)}`;
            // Update map with user location
        },
        (error) => {
            let message = 'Unable to get your location.';
            switch(error.code) {
                case error.PERMISSION_DENIED:
                    message = 'Location access denied by user.';
                    break;
                case error.POSITION_UNAVAILABLE:
                    message = 'Location information unavailable.';
                    break;
                case error.TIMEOUT:
                    message = 'Location request timed out.';
                    break;
            }
            errorEl.textContent = message;
            errorEl.style.display = 'block';
            statusEl.textContent = '';
        },
        {
            enableHighAccuracy: true,
            timeout: 10000,
            maximumAge: 300000
        }
    );
});
</script>

File Upload for Spatial Data:

<section class="data-upload">
    <h2>Upload Spatial Data</h2>
    <form id="data-upload-form" enctype="multipart/form-data">
        <div class="upload-area" id="upload-area">
            <label for="spatial-files">
                <span class="upload-icon" aria-hidden="true">📁</span>
                <span class="upload-text">
                    Drop GeoJSON, KML, or GPX files here, or click to browse
                </span>
            </label>
            <input type="file" 
                   id="spatial-files" 
                   name="spatial-files" 
                   multiple 
                   accept=".geojson,.kml,.gpx,.json"
                   class="visually-hidden">
        </div>
        
        <div class="file-list" id="file-list" role="list">
            <!-- Uploaded files appear here -->
        </div>
        
        <button type="submit" id="process-files" disabled>
            Process Files
        </button>
    </form>
    
    <div id="upload-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="display: none;">
        <div class="progress-bar"></div>
        <span class="progress-text">Processing...</span>
    </div>
</section>

<style>
.upload-area {
    border: 2px dashed #ccc;
    border-radius: 8px;
    padding: 2rem;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.3s;
}

.upload-area:hover, .upload-area.dragover {
    border-color: #007bff;
    background-color: #f8f9fa;
}

.file-list {
    margin: 1rem 0;
}

.file-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.5rem;
    border: 1px solid #ddd;
    border-radius: 4px;
    margin-bottom: 0.5rem;
}

.progress-bar {
    height: 20px;
    background-color: #e9ecef;
    border-radius: 10px;
    overflow: hidden;
}

.progress-bar::after {
    content: '';
    display: block;
    height: 100%;
    background-color: #007bff;
    width: var(--progress, 0%);
    transition: width 0.3s;
}
</style>

4.8. Performance Considerations and Optimization#

4.8.1. Resource Loading Optimization#

Structure HTML for optimal loading performance:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <!-- Critical resource hints -->
    <link rel="preconnect" href="https://demotiles.maplibre.org">
    <link rel="dns-prefetch" href="//api.openweathermap.org">
    <link rel="preload" href="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.css" as="style">
    <link rel="preload" href="/fonts/map-icons.woff2" as="font" type="font/woff2" crossorigin>
    
    <title>High Performance Map Application</title>
    
    <!-- Critical CSS inline for immediate rendering -->
    <style>
        .map-container { 
            width: 100vw; 
            height: 100vh; 
            background: #f5f5f5;
            position: relative;
        }
        .loading-screen { 
            position: absolute; 
            top: 50%; 
            left: 50%; 
            transform: translate(-50%, -50%);
            z-index: 1000;
        }
        .loading-spinner {
            width: 40px;
            height: 40px;
            border: 4px solid #f3f3f3;
            border-top: 4px solid #3498db;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
    
    <!-- Non-critical CSS loaded asynchronously -->
    <link rel="preload" href="styles/app.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
    <noscript><link rel="stylesheet" href="styles/app.css"></noscript>
    
    <link href="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.css" rel="stylesheet">
</head>
<body>
    <div class="map-container">
        <div id="map" role="application" aria-label="Interactive geographical map"></div>
        
        <div class="loading-screen" id="loading-screen">
            <div class="loading-spinner" aria-hidden="true"></div>
            <p>Loading map data...</p>
        </div>
        
        <!-- Control panels -->
        <aside class="map-controls" id="map-controls">
            <h2 class="visually-hidden">Map Controls</h2>
            <!-- Controls loaded dynamically -->
        </aside>
    </div>
    
    <!-- Scripts loaded with appropriate strategies -->
    <script>
        // Inline critical JavaScript for immediate execution
        window.mapConfig = {
            center: [-74.006, 40.7128],
            zoom: 12,
            style: 'https://demotiles.maplibre.org/style.json'
        };
        
        // Feature detection
        window.supportsWebGL = (() => {
            try {
                const canvas = document.createElement('canvas');
                return !!(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
            } catch (e) {
                return false;
            }
        })();
    </script>
    
    <!-- Load mapping library asynchronously -->
    <script async src="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.js"></script>
    
    <!-- Application code loaded as ES modules -->
    <script type="module">
        import { initializeMap } from './js/map-init.js';
        import { setupControls } from './js/controls.js';
        
        // Wait for both DOM and mapping library
        document.addEventListener('DOMContentLoaded', () => {
            if (window.maplibregl) {
                initializeApp();
            } else {
                window.addEventListener('maplibregl-loaded', initializeApp);
            }
        });
        
        function initializeApp() {
            if (!window.supportsWebGL) {
                // Fallback for devices without WebGL support
                import('./js/fallback-map.js').then(module => {
                    module.initializeFallback();
                });
                return;
            }
            
            initializeMap(window.mapConfig)
                .then(map => {
                    setupControls(map);
                    document.getElementById('loading-screen').style.display = 'none';
                })
                .catch(error => {
                    console.error('Map initialization failed:', error);
                    showErrorMessage('Failed to load map. Please refresh to try again.');
                });
        }
        
        function showErrorMessage(message) {
            const loadingScreen = document.getElementById('loading-screen');
            loadingScreen.innerHTML = `
                <div role="alert" class="error-message">
                    <h2>Unable to Load Map</h2>
                    <p>${message}</p>
                    <button onclick="location.reload()">Retry</button>
                </div>
            `;
        }
    </script>
</body>
</html>

4.8.2. Content Security Policy#

Implement CSP headers for security without breaking mapping functionality:

<meta http-equiv="Content-Security-Policy" content="
    default-src 'self';
    script-src 'self' 'unsafe-inline' https://unpkg.com;
    style-src 'self' 'unsafe-inline' https://unpkg.com;
    img-src 'self' data: https://*.tile.openstreetmap.org https://demotiles.maplibre.org;
    connect-src 'self' https://api.openweathermap.org https://demotiles.maplibre.org;
    font-src 'self' data:;
    worker-src 'self' blob:;
">

4.9. Cross-Browser Compatibility#

4.9.1. Feature Detection and Polyfills#

Ensure compatibility across different browsers:

<script>
// Feature detection for critical APIs
window.browserSupport = {
    webgl: (() => {
        try {
            const canvas = document.createElement('canvas');
            return !!(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
        } catch (e) {
            return false;
        }
    })(),
    
    geolocation: 'geolocation' in navigator,
    
    fileAPI: window.File && window.FileReader && window.FileList && window.Blob,
    
    localStorage: (() => {
        try {
            const test = 'test';
            localStorage.setItem(test, test);
            localStorage.removeItem(test);
            return true;
        } catch (e) {
            return false;
        }
    })(),
    
    intersectionObserver: 'IntersectionObserver' in window,
    
    webWorkers: typeof Worker !== 'undefined'
};

// Load polyfills for missing features
if (!window.browserSupport.intersectionObserver) {
    const script = document.createElement('script');
    script.src = 'https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver';
    document.head.appendChild(script);
}
</script>

<!-- Conditional loading for older browsers -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->

<noscript>
    <div class="noscript-warning">
        <h2>JavaScript Required</h2>
        <p>This mapping application requires JavaScript to function properly. 
           Please enable JavaScript in your browser settings.</p>
        <p><a href="static-map.html">View static map alternative</a></p>
    </div>
</noscript>

4.10. Summary#

Proper HTML structure is fundamental to creating accessible, maintainable Web GIS applications. By using semantic elements, implementing proper accessibility features, and following best practices for map containers, you create a solid foundation for interactive geospatial applications.

The next chapter will explore CSS essentials for styling map interfaces and creating responsive layouts.

4.11. Exercises#

4.11.1. Exercise 4.1: Semantic HTML Structure#

Objective: Create a well-structured, semantic HTML foundation for a Web GIS application.

Instructions:

  1. Design a layout for a city services mapping application that includes:

    • A header with navigation

    • A main map area

    • A sidebar with layer controls

    • An information panel for displaying feature details

    • A footer with attribution and links

  2. Implement the structure using semantic HTML elements:

    • Use appropriate elements (header, nav, main, aside, footer)

    • Add proper ARIA attributes for accessibility

    • Include placeholder content for each section

  3. Validate your HTML:

    • Use the W3C HTML validator

    • Fix any validation errors

    • Test with a screen reader or accessibility checker

Deliverable: A complete HTML file with semantic structure and no validation errors.

4.11.2. Exercise 4.2: Form Design for Map Interaction#

Objective: Create accessible forms for map search and filtering functionality.

Instructions:

  1. Design a search form that includes:

    • Location search input with autocomplete

    • Category filter with checkboxes

    • Date range picker

    • Radius/distance selector

  2. Implement with proper accessibility:

    • Add appropriate labels and fieldsets

    • Include helpful placeholder text and descriptions

    • Use proper input types for different data

    • Add validation attributes

  3. Test usability:

    • Navigate using only the keyboard

    • Test with browser built-in accessibility tools

    • Verify form submission behavior

Deliverable: An accessible form that integrates well with your map layout.

4.11.3. Exercise 4.3: Responsive Map Container Design#

Objective: Create map containers that work across different screen sizes.

Instructions:

  1. Create multiple container layouts:

    • Full-screen map for mobile

    • Split-screen map and sidebar for tablet

    • Multi-panel layout for desktop

  2. Implement responsive behavior:

    • Use CSS media queries to switch between layouts

    • Ensure map containers maintain proper aspect ratios

    • Test on different screen sizes

  3. Add interactive elements:

    • Floating action buttons for mobile

    • Collapsible sidebars for tablets

    • Fixed toolbars for desktop

Deliverable: A responsive HTML layout that adapts appropriately to different screen sizes.

4.11.4. Exercise 4.4: Accessibility Implementation#

Objective: Make a Web GIS interface fully accessible to users with disabilities.

Instructions:

  1. Audit an existing map interface for accessibility issues:

    • Use automated accessibility testing tools

    • Test with keyboard navigation only

    • Try using a screen reader

  2. Implement accessibility improvements:

    • Add appropriate ARIA labels and descriptions

    • Ensure proper focus management

    • Provide alternative content for visual elements

    • Add skip links for navigation

  3. Create accessible map alternatives:

    • Data table showing map information

    • Text description of spatial relationships

    • Audio descriptions for important features

Deliverable: An accessibility audit report and improved HTML implementation.

4.11.5. Exercise 4.5: Performance Optimization#

Objective: Optimize HTML structure for better loading and rendering performance.

Instructions:

  1. Analyze loading performance:

    • Use browser developer tools to measure page load times

    • Identify render-blocking resources

    • Measure first contentful paint and largest contentful paint

  2. Implement optimizations:

    • Add proper preloading hints for critical resources

    • Optimize image loading with appropriate attributes

    • Minimize and compress HTML content

    • Implement critical CSS inlining

  3. Test improvements:

    • Measure performance before and after optimizations

    • Test on slow network connections

    • Verify improvements on different devices

Deliverable: Performance analysis report and optimized HTML implementation.

4.11.6. Exercise 4.6: Multi-page Application Structure#

Objective: Design HTML structure for a complex, multi-page Web GIS application.

Instructions:

  1. Plan the application structure for a municipal GIS portal with:

    • Main map viewer page

    • Data catalog page

    • User account/profile page

    • Help and documentation pages

    • Administrative dashboard

  2. Create consistent page templates:

    • Design a base template with common elements

    • Create page-specific variations

    • Ensure consistent navigation across pages

  3. Implement progressive enhancement:

    • Start with basic HTML functionality

    • Layer on enhanced features

    • Ensure graceful degradation

Deliverable: A set of HTML pages demonstrating consistent structure and navigation for a complete Web GIS application.

Reflection Questions:

  • How does semantic HTML improve both accessibility and SEO for Web GIS applications?

  • What are the trade-offs between single-page and multi-page applications for Web GIS?

  • How would you approach HTML structure differently for mobile-first vs desktop-first design?

  • What role does HTML play in the overall performance of Web GIS applications?

4.12. Further Reading#