H M < >
CMU422: Web Design - Session 7

JavaScript Events and Interactivity

Professional Web Design and Development

James Williams

Birmingham Newman University

jwilliams@staff.newman.ac.uk

3-hour session

Learning Objectives

  • Understand JavaScript event handling
  • Master different types of events
  • Create interactive user interfaces
  • Handle form submissions and validation
  • Build responsive and accessible interactions

What are JavaScript Events?

Events are actions or occurrences that happen in the browser
  • User interactions (clicks, key presses, mouse movements)
  • Browser actions (page load, resize, scroll)
  • Form submissions and input changes
  • Network events (AJAX responses)
// Event example
button.addEventListener('click', function() {
console.log('Button clicked!');
});

Common Event Types

// Mouse events
'click', 'dblclick', 'mouseenter', 'mouseleave'
// Keyboard events
'keydown', 'keyup', 'keypress'
// Form events
'submit', 'change', 'input', 'focus', 'blur'
// Document events
'load', 'DOMContentLoaded', 'resize', 'scroll'
  • Mouse events for user interactions
  • Keyboard events for text input
  • Form events for data handling
  • Document events for page lifecycle

Event Handling Methods

// Method 1: addEventListener (recommended)
element.addEventListener('click', function(event) {
console.log('Clicked!');
});
// Method 2: Inline event handlers
<button onclick="handleClick()">Click me</button>
// Method 3: Event properties
element.onclick = function() {
console.log('Clicked!');
};
  • addEventListener is the modern approach
  • Allows multiple event listeners
  • Better control over event handling

Try It: Interactive DOM Manipulation

Change color:
Click the buttons to manipulate me!

This demo shows DOM manipulation, event handling, and dynamic styling!

The Event Object

element.addEventListener('click', function(event) {
console.log(event.type); // 'click'
console.log(event.target); // Element that was clicked
console.log(event.clientX); // Mouse X position
console.log(event.clientY); // Mouse Y position
event.preventDefault(); // Prevent default behavior
event.stopPropagation(); // Stop event bubbling
});
  • Contains information about the event
  • Provides methods to control event behavior
  • Automatically passed to event handlers

Event Bubbling and Capturing

// Event bubbling (default)
parent.addEventListener('click', function() {
console.log('Parent clicked');
});
// Event capturing
parent.addEventListener('click', function() {
console.log('Parent clicked');
}, true); // true enables capturing
// Stop propagation
child.addEventListener('click', function(event) {
event.stopPropagation();
});
  • Events bubble up from child to parent
  • Capturing goes from parent to child
  • Use stopPropagation() to prevent bubbling

Form Events

const form = document.querySelector('form');
// Form submission
form.addEventListener('submit', function(event) {
event.preventDefault();
// Handle form data
});
// Input changes
input.addEventListener('input', function(event) {
console.log('Input value:', event.target.value);
});
// Focus events
input.addEventListener('focus', function() {
this.style.borderColor = 'blue';
});
  • submit: Form submission
  • input: Value changes
  • focus/blur: Field focus
  • change: Value confirmed

Keyboard Events

document.addEventListener('keydown', function(event) {
console.log('Key pressed:', event.key);
console.log('Key code:', event.code);
// Check for specific keys
if (event.key === 'Enter') {
console.log('Enter pressed!');
}
// Check for modifier keys
if (event.ctrlKey && event.key === 's') {
event.preventDefault();
console.log('Save shortcut!');
}
});
  • keydown: Key pressed down
  • keyup: Key released
  • keypress: Character input
  • Check for specific keys and modifiers

Mouse Events

element.addEventListener('click', function(event) {
console.log('Click at:', event.clientX, event.clientY);
});
element.addEventListener('mouseenter', function() {
this.style.backgroundColor = 'yellow';
});
element.addEventListener('mouseleave', function() {
this.style.backgroundColor = 'white';
});
element.addEventListener('mousemove', function(event) {
// Track mouse movement
});
  • click: Single click
  • dblclick: Double click
  • mouseenter/mouseleave: Hover effects
  • mousemove: Mouse tracking

Event Delegation

// Instead of adding listeners to each item
const list = document.querySelector('ul');
list.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
console.log('List item clicked:',
event.target.textContent);
}
});
// Works for dynamically added items too
  • Attach event listener to parent element
  • Use event.target to identify specific elements
  • Efficient for multiple similar elements
  • Works with dynamically added content

Client-Side Form Validation

form.addEventListener('submit', function(event) {
event.preventDefault();
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
if (!email.includes('@')) {
showError('Please enter a valid email');
return;
}
if (password.length < 8) {
showError('Password must be at least 8
characters');
return;
}
// Form is valid, submit it
this.submit();
});
  • Validate before form submission
  • Provide immediate user feedback
  • Always validate on server side too
  • Use HTML5 validation attributes

AJAX and Fetch API

// Fetch data from server
fetch('/api/data')
.then(response => response.json())
.then(data => {
  console.log('Data received:', data);
  updateUI(data);
})
.catch(error => {
  console.error('Error:', error);
});
// POST data to server
fetch('/api/submit', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(data)
});
  • Fetch API for HTTP requests
  • Promise-based approach
  • Handle success and error cases
  • Update UI with received data

Local Storage

// Store data
localStorage.setItem('username', 'john');
localStorage.setItem('preferences', JSON.stringify({theme: 'dark'}));
// Retrieve data
const username = localStorage.getItem('username');
const preferences = JSON.parse(localStorage.getItem('preferences'));
// Remove data
localStorage.removeItem('username');
localStorage.clear(); // Remove all
// Session storage (cleared when tab closes)
sessionStorage.setItem('temp', 'value');
  • Store data in browser
  • Persists between sessions
  • Limited to strings (use JSON for objects)
  • 5-10MB storage limit

Animation and Transitions

// CSS transitions
element.style.transition = 'all 0.3s ease';
element.style.transform = 'scale(1.1)';
// JavaScript animations
function animate(element, duration) {
const start = performance.now();
function update(currentTime) {
const elapsed = currentTime - start;
const progress = Math.min(elapsed / duration, 1);
element.style.opacity = progress;
if (progress < 1) {
requestAnimationFrame(update);
}
}
requestAnimationFrame(update);
}
  • CSS transitions for simple animations
  • JavaScript for complex animations
  • Use requestAnimationFrame for smooth animations
  • Consider performance and accessibility

Accessibility Best Practices

  • Use semantic HTML elements
  • Provide keyboard navigation
  • Add ARIA attributes when needed
  • Ensure focus management
  • Test with screen readers
  • Provide alternative text for images
  • Use sufficient color contrast
// Keyboard navigation
element.addEventListener('keydown', function(event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleClick();
}
});
// ARIA attributes
element.setAttribute('aria-label', 'Close dialog');
element.setAttribute('role', 'button');

Error Handling

// Try-catch for synchronous code
try {
  const result = riskyOperation();
  console.log(result);
} catch (error) {
console.error('Error occurred:', error.message);
showUserFriendlyError();
}
// Promise error handling
fetch('/api/data')
.then(response => {
  if (!response.ok) {
    throw new Error('Network response was not
    ok');
  }
  return response.json();
})
.catch(error => {
  console.error('Fetch error:', error);
  showErrorMessage(error.message);
});
  • Handle errors gracefully
  • Provide user-friendly error messages
  • Log errors for debugging
  • Use try-catch and promise error handling

Typed.js

Create animated typewriter text effects.

// Initialize Typed.js
new Typed('#typed-demo', {
  strings: ['Hello World!', 'Welcome to Typed.js', 'Amazing typewriter effect!'],
  typeSpeed: 50,
  backSpeed: 30,
  loop: true
});

Live Demo

Tippy.js

Beautiful tooltips and popovers with custom styling.

// Initialize Tippy.js
tippy('#my-button', {
  content: 'This is a tooltip!',
  theme: 'light',
  animation: 'scale'
});

Try It: Hover over the buttons

SweetAlert2

Beautiful, responsive, customizable popup boxes.

// Show a SweetAlert
Swal.fire({
  title: 'Success!',
  text: 'Your action was completed.',
  icon: 'success',
  confirmButtonText: 'OK'
});

Try It: Click the buttons

Lodash

Utility library for array/object manipulation and data processing.

// Lodash utilities
const arr = [1, 2, 2, 3, 3, 3, 4];
_.uniq(arr);  // [1, 2, 3, 4]

const users = [{name: 'John', age: 30}, {name: 'Jane', age: 25}];
_.sortBy(users, 'age');  // Sort by age

Live Examples

Day.js (Moment.js alternative)

Lightweight library for date formatting and manipulation.

// Format dates with Day.js
dayjs().format('MMMM D, YYYY');  // "January 15, 2024"
dayjs().format('h:mm A');        // "3:45 PM"
dayjs().add(7, 'day').format('MMM DD');  // Add 7 days

Live Date Formatting

Clipboard.js

Copy text to clipboard without Flash.

<!-- HTML -->
<input id="copy-input" value="Copy this text" />
<button data-clipboard-target="#copy-input">Copy</button>

<script>
// Initialize Clipboard.js
new ClipboardJS('button');
</script>

Try It: Click to copy

Toastify.js

Simple toast notifications for user feedback.

// Show a toast notification
Toastify({
  text: "This is a toast notification!",
  duration: 3000,
  gravity: "top",
  position: "right",
  backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)"
}).showToast();

Try It: Click to show toast

Three.js

Create stunning 3D graphics and animations in the browser.

// Create a 3D scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x2196f3 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

Live 3D Animation

p5.js

Creative coding library for interactive art and animations.

// p5.js sketch
function setup() {
  createCanvas(400, 200);
}

function draw() {
  background(30);
  fill(255, 100, 150);
  ellipse(mouseX, mouseY, 30, 30);
}

Try It: Move your mouse over the canvas

GSAP (GreenSock Animation Platform)

Professional-grade JavaScript animation library.

// GSAP animation
gsap.to('#box', {
  duration: 1.2,
  y: -30,
  repeat: -1,
  yoyo: true,
  ease: 'power1.inOut'
});

Live Animation

Leaflet

Interactive maps for mobile and desktop.

// Create a map
const map = L.map('map').setView([52.489, -1.898], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
L.marker([52.489, -1.898]).addTo(map)
  .bindPopup('Birmingham Newman University');

Interactive Map

Chart.js

Simple yet flexible JavaScript charting library.

// Create a chart
new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
    datasets: [{
      label: 'Sales',
      data: [12, 19, 3, 5, 2]
    }]
  }
});

Live Chart

Task 1: Interactive Form

Instructions:

  1. Create a new HTML file called interactive-form.html
  2. Build a registration form with:
    • Name, email, password, and confirm password fields
    • Real-time validation as user types
    • Password strength indicator
    • Submit button with loading state
  3. Add event listeners for:
    • Input validation on blur and input events
    • Form submission with preventDefault
    • Password strength calculation
  4. Use local storage to remember form data
  5. Add visual feedback for validation states

Time: 45 minutes

This task will help you understand form events and validation

Break Time

15 Minutes

Take a break, ask questions, or catch up on the previous task.

Next: Secondary slides and Task 2

Event Handling Best Practices

  • Use addEventListener instead of inline handlers
  • Remove event listeners when elements are destroyed
  • Use event delegation for multiple elements
  • Prevent default behavior when appropriate
  • Handle errors gracefully
  • Consider performance for frequent events
  • Test across different browsers
// Remove event listener
const handler = function() { console.log('clicked'); };
element.addEventListener('click', handler);
element.removeEventListener('click', handler);
// Throttle frequent events
let timeout;
element.addEventListener('scroll', function() {
clearTimeout(timeout);
timeout = setTimeout(handleScroll, 100);
});

Performance Considerations

  • Throttle or debounce frequent events
  • Use event delegation for many elements
  • Avoid inline event handlers
  • Remove unused event listeners
  • Use passive event listeners when possible
  • Consider using Intersection Observer for scroll events
// Passive event listener
element.addEventListener('touchstart', handler, { passive: true });
// Intersection Observer
const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
    }
  });
});

Debugging Event Issues

  • Use browser developer tools
  • Check event listener attachment
  • Verify event target and currentTarget
  • Monitor event propagation
  • Check for JavaScript errors
  • Test event timing
// Debug event listeners
element.addEventListener('click', function(event) {
console.log('Event target:', event.target);
console.log('Current target:', event.currentTarget);
console.log('Event phase:', event.eventPhase);
console.trace('Event call stack');
});
// Monitor all events
const originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
console.log('Adding listener:', type, 'to', this);
return originalAddEventListener.call(this, type, listener,
options);
};

Mobile-Specific Events

// Touch events
element.addEventListener('touchstart', function(event) {
console.log('Touch started');
event.preventDefault(); // Prevent default touch behavior
});
element.addEventListener('touchmove', function(event) {
console.log('Touch moved');
});
element.addEventListener('touchend', function(event) {
console.log('Touch ended');
});
// Device orientation
window.addEventListener('orientationchange', function() {
console.log('Orientation changed');
});
  • Touch events for mobile interactions
  • Orientation change events
  • Consider touch targets (minimum 44px)
  • Handle both touch and mouse events

Task 2: Interactive Dashboard

Instructions:

  1. Create a new HTML file called interactive-dashboard.html
  2. Build an interactive dashboard with:
    • Navigation: Tabbed interface with smooth transitions
    • Widgets: Collapsible panels with toggle functionality
    • Charts: Simple data visualization (use CSS or basic canvas)
    • Search: Real-time search with debouncing
    • Settings: Theme toggle and preferences
  3. Implement event handling for:
    • Tab switching with keyboard navigation
    • Widget collapse/expand animations
    • Search input with live filtering
    • Theme switching with local storage
  4. Add accessibility features (ARIA attributes, keyboard support)
  5. Make it responsive for mobile devices

Time: 45 minutes

This task will help you understand complex event handling and UI interactions

Session Summary

  • Events are the foundation of interactive web applications
  • Use modern event handling methods
  • Implement proper form validation
  • Consider accessibility and performance
  • Handle errors gracefully
  • Test across different devices and browsers

Next Session:

JavaScript Advanced Concepts - Functions, objects, and modern JavaScript