Svelte
Core Concepts

Handling DOM Events

Introduction

Unhead provides support for native DOM event handling, allowing you to attach event listeners to document elements through head management. This feature enables you to execute code in response to various browser events like window resizing, navigation changes, or print events.

This guide focuses on non-script DOM events, particularly those attached to the body element. For script-specific event handling and loading, please refer to the Loading Scripts guide which covers the more powerful useScript() composable.

Resource Loading for Stylesheets

For non-script resources like stylesheets, Unhead supports standard HTTP-related events. However, these are generally only needed in special cases.

import { useHead } from '@unhead/svelte'

useHead({
  link: [
    {
      rel: 'stylesheet',
      href: '/assets/critical.css',
      onload: () => {
        console.log('Critical CSS loaded')
        // Load non-critical resources
        loadDeferredResources()
      },
      onerror: () => {
        console.error('Failed to load stylesheet - using fallback styles')
        applyFallbackStyles()
      }
    }
  ]
}, { mode: 'client' })
Remember that stylesheet events should also be used with { mode: 'client' } to ensure they work correctly with SSR.

Document and Window Events

Unhead allows you to attach event handlers to the document body through the bodyAttrs property. These events effectively let you respond to document and window-level events.

Per the HTML specification, body event handlers are automatically proxied to the window object for better browser compatibility.

Supported Body Events

These events are only supported with bodyAttrs and provide access to various browser lifecycle events:

import { useHead } from '@unhead/svelte'

// Types for body event handlers
interface BodyEvents {
  // Printing events
  onafterprint?: string | ((el: Element) => void) // After print dialog closes
  onbeforeprint?: string | ((el: Element) => void) // Before print dialog opens

  // Page lifecycle events
  onbeforeunload?: string | ((el: Element) => void) // Before page is unloaded
  onload?: string | ((el: Element) => void) // Page finished loading
  onunload?: string | ((el: Element) => void) // Page is being unloaded

  // Navigation events
  onhashchange?: string | ((el: Element) => void) // URL hash has changed
  onpagehide?: string | ((el: Element) => void) // User navigates away
  onpageshow?: string | ((el: Element) => void) // User navigates to page
  onpopstate?: string | ((el: Element) => void) // Window history changes

  // Connection events
  onoffline?: string | ((el: Element) => void) // Browser goes offline
  ononline?: string | ((el: Element) => void) // Browser goes online

  // Other events
  onerror?: string | ((el: Element) => void) // Error occurs
  onmessage?: string | ((el: Element) => void) // Message is received
  onresize?: string | ((el: Element) => void) // Window is resized
  onstorage?: string | ((el: Element) => void) // Web Storage is updated
}
Remember that these events should generally be used with { mode: 'client' } to ensure they work properly with server-side rendering.

Practical Examples

Responding to Window Resize

Track window size changes and update application state:

import { useHead } from '@unhead/svelte'

useHead({
  bodyAttrs: {
    onresize: (e) => {
      console.log('Window resized', e)
      // Update responsive state, recalculate layouts, etc.
    }
  }
}, { mode: 'client' })

Tracking Page Load Performance

Measure and report page load performance metrics:

import { useHead } from '@unhead/svelte'

useHead({
  bodyAttrs: {
    onload: () => {
      // Report page load timing data
      if (window.performance) {
        const perfData = window.performance.timing
        const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart
        console.log(`Page loaded in ${pageLoadTime}ms`)

        // Send to analytics
        if (window.analytics) {
          window.analytics.track('Page Load Time', {
            milliseconds: pageLoadTime
          })
        }
      }
    }
  }
}, { mode: 'client' })

Handling Offline Status

Notify users when they lose internet connection:

import { useHead } from '@unhead/svelte'

useHead({
  bodyAttrs: {
    onoffline: () => {
      // Show offline notification
      showNotification('You are currently offline. Some features may be unavailable.')
    },
    ononline: () => {
      // Show back online notification
      showNotification('You are back online!')
      // Resync data if needed
      syncData()
    }
  }
}, { mode: 'client' })

Best Practices for DOM Events

  1. Always use { mode: 'client' } when working with event handlers to ensure proper hydration
    useHead({
      bodyAttrs: {
        onresize: () => handleResize()
      }
    }, { mode: 'client' }) // Critical for event handlers
    
  2. Keep event handlers lightweight to avoid performance issues
    // Good: Lightweight handler that delegates complex logic
    useHead({
      bodyAttrs: {
        onresize: () => requestAnimationFrame(recalculateLayout)
      }
    }, { mode: 'client' })
    
  3. Use useScript() for script-related events rather than direct script event handlers
    // Prefer this approach for scripts
    useScript({
      src: 'https://example.com/analytics.js',
      onLoaded: api => api.initialize()
    })
    
  4. Clean up resources in event handlers to prevent memory leaks
    useHead({
      bodyAttrs: {
        onload: () => {
          const observer = new ResizeObserver(handleResize)
          observer.observe(document.body)
    
          // Store cleanup function for framework to use
          return () => observer.disconnect()
        }
      }
    }, { mode: 'client' })
    

When to Use DOM Events vs useScript

ScenarioRecommended Approach
Script loading and interactionuseScript()
Window resize handlingDOM events with bodyAttrs
Page lifecycle eventsDOM events with bodyAttrs
Print eventsDOM events with bodyAttrs
Stylesheet loadingDOM events with link tags
Online/offline detectionDOM events with bodyAttrs
Did this page help you?