JavaScript and TypeScript course Β· Module 8: Events and the DOM

The Event Object and Its Properties

9 min read
In this lesson8

"Every sensor report in the park," Ray Arnold opens the detailed system documentation, "contains precise information: where, when, what type of event, with what intensity. Similarly, the Event object in JavaScript is a complete report about what happened in the browser."

Ray points to a monitor showing data from a motion sensor near the T-Rex enclosure. "See? Time: 14:23:47, location: sector B-7, movement type: fast, direction: north. Every piece of information is crucial for the proper response."

What Is the Event Object?

Every event in JavaScript automatically creates an Event object, which is passed to the handler function as its first parameter. This object contains all relevant information about the event - from cursor position, through pressed keys, to the element that triggered the event.

Basic Anatomy of the Event Object

"Let's start with the fundamentals," says Ray, opening the diagnostic panel.

1const parkMap = document.getElementById('interactive-map');
2
3parkMap.addEventListener('click', function(event) {
4  // event is an object containing all the information
5
6  console.log('=== EVENT REPORT ===');
7  console.log('Event type:', event.type); // 'click'
8  console.log('Timestamp:', event.timeStamp); // time in ms since page load
9  console.log('Event phase:', event.eventPhase); // 1=capture, 2=target, 3=bubble
10
11  // Click position information
12  console.log(`Clicked at position: X=${event.clientX}, Y=${event.clientY}`);
13
14  // The element that was clicked
15  console.log('Clicked element:', event.target);
16  console.log('Tag name:', event.target.tagName);
17  console.log('Element ID:', event.target.id);
18
19  // The element the event listener is attached to
20  console.log('Element with listener:', event.currentTarget);
21
22  // Can the default action be canceled?
23  console.log('Cancelable:', event.cancelable);
24
25  // Does the event bubble?
26  console.log('Bubbles:', event.bubbles);
27});

Position Properties - Coordinate Systems

"In Jurassic Park," Ray explains, "we use different coordinate systems - GPS, local sector coordinates, positions relative to gates. Similarly, JavaScript offers several coordinate systems for mouse position."

Different Types of Coordinates

1const viewingPlatform = document.getElementById('platform');
2
3viewingPlatform.addEventListener('click', function(event) {
4  console.log('=== COORDINATE SYSTEMS ===');
5
6  // 1. clientX/Y - position relative to the browser window (viewport)
7  console.log('Viewport:', event.clientX, event.clientY);
8  console.log('Like position relative to your control monitor');
9
10  // 2. pageX/Y - position relative to the entire page (accounts for scroll)
11  console.log('Page:', event.pageX, event.pageY);
12  console.log('Like coordinates on the full park map');
13
14  // 3. screenX/Y - position relative to the entire screen
15  console.log('Screen:', event.screenX, event.screenY);
16  console.log('Like GPS position - absolute location');
17
18  // 4. offsetX/Y - position relative to the clicked element
19  console.log('Offset:', event.offsetX, event.offsetY);
20  console.log('Like position within a specific enclosure');
21
22  // Practical use - marking area on the map
23  markLocationOnMap(event.pageX, event.pageY);
24  showCoordinatesInfo(event.clientX, event.clientY);
25});
26
27function markLocationOnMap(x, y) {
28  const marker = document.createElement('div');
29  marker.className = 'location-marker';
30  marker.style.left = x + 'px';
31  marker.style.top = y + 'px';
32  document.body.appendChild(marker);
33
34  console.log(`Location marked on map: (${x}, ${y})`);
35}
36
37function showCoordinatesInfo(clientX, clientY) {
38  const tooltip = document.getElementById('coordinates-tooltip');
39  tooltip.textContent = `Position: X=${clientX}, Y=${clientY}`;
40  tooltip.style.left = (clientX + 10) + 'px';
41  tooltip.style.top = (clientY + 10) + 'px';
42  tooltip.style.display = 'block';
43}

target vs currentTarget - The Key Difference

"This is one of the most common mistakes," Ray warns. "Confusing target with currentTarget. It's like confusing the dinosaur that escaped with the enclosure it escaped from."

Understanding the Difference

1// HTML:
2// <div id="enclosure" class="dino-enclosure">
3//   <div class="info-panel">
4//     <h3>T-Rex Enclosure</h3>
5//     <button class="feed-button">Feed</button>
6//     <button class="status-button">Status</button>
7//   </div>
8// </div>
9
10const enclosure = document.getElementById('enclosure');
11
12enclosure.addEventListener('click', function(event) {
13  // target - the element that was ACTUALLY clicked
14  console.log('Target (clicked element):', event.target);
15  console.log('Target tag:', event.target.tagName);
16
17  // currentTarget - the element the listener is attached to
18  console.log('CurrentTarget (element with listener):', event.currentTarget);
19  console.log('CurrentTarget ID:', event.currentTarget.id); // always 'enclosure'
20
21  // Practical example
22  if (event.target.classList.contains('feed-button')) {
23    console.log('Feed button clicked');
24    console.log('In enclosure:', event.currentTarget.id);
25    feedDinosaur(event.currentTarget.id);
26  } else if (event.target.classList.contains('status-button')) {
27    console.log('Status button clicked');
28    console.log('For enclosure:', event.currentTarget.id);
29    showEnclosureStatus(event.currentTarget.id);
30  } else {
31    console.log('Clicked somewhere in the enclosure, but not on a button');
32  }
33});
34
35function feedDinosaur(enclosureId) {
36  console.log(`Starting feeding in enclosure: ${enclosureId}`);
37  console.log('Activating security system...');
38  console.log('Dispensing food...');
39  console.log('Feeding completed');
40}
41
42function showEnclosureStatus(enclosureId) {
43  console.log(`=== ENCLOSURE STATUS ${enclosureId} ===`);
44  console.log('Temperature: 28Β°C');
45  console.log('Humidity: 65%');
46  console.log('Last feeding: 12:30');
47  console.log('Dinosaur activity: Normal');
48}

Modifier Keys - Multi-Function Actions

"In critical systems," Ray explains, "we often need key combinations for security. Like two keys for the armory. The Event object shows us which keys are pressed."

Detecting Modifier Keys

1const dinosaurCard = document.querySelectorAll('.dinosaur-card');
2
3dinosaurCard.forEach(card => {
4  card.addEventListener('click', function(event) {
5    console.log('=== KEY ANALYSIS ===');
6    console.log('Ctrl pressed:', event.ctrlKey);
7    console.log('Shift pressed:', event.shiftKey);
8    console.log('Alt pressed:', event.altKey);
9    console.log('Meta (Cmd/Win) pressed:', event.metaKey);
10
11    const dinoId = this.dataset.dinoId;
12    const dinoName = this.dataset.dinoName;
13
14    // Different actions depending on key combinations
15    if (event.ctrlKey && event.shiftKey) {
16      // Ctrl + Shift + Click = Advanced options
17      console.log('Opening advanced options for:', dinoName);
18      openAdvancedOptions(dinoId);
19
20    } else if (event.ctrlKey) {
21      // Ctrl + Click = Add to multiple selection
22      console.log('Adding to selection:', dinoName);
23      addToSelection(dinoId);
24      this.classList.toggle('selected');
25
26    } else if (event.shiftKey) {
27      // Shift + Click = Select range
28      console.log('Range selection to:', dinoName);
29      selectRange(dinoId);
30
31    } else if (event.altKey) {
32      // Alt + Click = Quick preview
33      console.log('Quick preview:', dinoName);
34      showQuickPreview(dinoId);
35
36    } else {
37      // Normal click
38      console.log('Selected:', dinoName);
39      clearSelection();
40      selectSingle(dinoId);
41      this.classList.add('selected');
42    }
43  });
44});
45
46// Helper function implementations
47let selectedDinosaurs = [];
48
49function addToSelection(dinoId) {
50  if (!selectedDinosaurs.includes(dinoId)) {
51    selectedDinosaurs.push(dinoId);
52  } else {
53    selectedDinosaurs = selectedDinosaurs.filter(id => id !== dinoId);
54  }
55  console.log('Selected dinosaurs:', selectedDinosaurs);
56}
57
58function clearSelection() {
59  selectedDinosaurs = [];
60  document.querySelectorAll('.dinosaur-card.selected').forEach(card => {
61    card.classList.remove('selected');
62  });
63  console.log('Selection cleared');
64}
65
66function selectSingle(dinoId) {
67  selectedDinosaurs = [dinoId];
68  console.log('Single selection:', dinoId);
69}
70
71function selectRange(endId) {
72  console.log('Implementing range selection to:', endId);
73  // Range selection logic between last selected and current
74}

Mouse Button Information

"Different sensors react to different types of signals," Ray shows a panel with three buttons. "Similarly, we can react differently to the left, right, and middle mouse buttons."

Detecting the Mouse Button

1const interactiveMap = document.getElementById('park-map');
2
3interactiveMap.addEventListener('mousedown', function(event) {
4  console.log('=== MOUSE BUTTON INFORMATION ===');
5  console.log('Button:', event.button);
6  console.log('0 = left, 1 = middle, 2 = right');
7  console.log('Buttons (bitmask):', event.buttons);
8
9  const clickX = event.clientX;
10  const clickY = event.clientY;
11
12  switch(event.button) {
13    case 0:
14      // Left button - standard action
15      console.log('Left button: Selecting location');
16      selectLocation(clickX, clickY);
17      break;
18
19    case 1:
20      // Middle button - new tab/window
21      console.log('Middle button: Opening in new window');
22      event.preventDefault();
23      openInNewWindow(clickX, clickY);
24      break;
25
26    case 2:
27      // Right button - context menu
28      console.log('Right button: Context menu');
29      showContextMenu(clickX, clickY);
30      break;
31
32    default:
33      console.log('Unknown button:', event.button);
34  }
35});
36
37// Blocking the default context menu
38interactiveMap.addEventListener('contextmenu', function(event) {
39  event.preventDefault();
40  console.log('Default menu blocked - using custom menu');
41});
42
43function selectLocation(x, y) {
44  console.log(`Location selected: (${x}, ${y})`);
45  highlightArea(x, y);
46}
47
48function showContextMenu(x, y) {
49  const menu = document.getElementById('custom-context-menu');
50  menu.style.left = x + 'px';
51  menu.style.top = y + 'px';
52  menu.style.display = 'block';
53
54  console.log('Context menu displayed at position:', x, y);
55}

Event Object Methods

"Besides properties," Ray shows the next screen, "the Event object also has methods - tools for controlling event behavior."

preventDefault() - Blocking the Default Action

1// Example 1: Custom form validation
2const securityForm = document.getElementById('security-access-form');
3
4securityForm.addEventListener('submit', function(event) {
5  event.preventDefault(); // BLOCK default form submission
6
7  console.log('Form submission intercepted');
8
9  const formData = new FormData(securityForm);
10  const accessCode = formData.get('access-code');
11  const securityLevel = formData.get('security-level');
12
13  // Custom validation
14  if (validateAccessCode(accessCode)) {
15    console.log('Access code correct');
16    grantAccess(securityLevel);
17  } else {
18    console.error('Invalid access code!');
19    showError('The access code is invalid');
20  }
21});
22
23// Example 2: Custom navigation
24const navigationLinks = document.querySelectorAll('a.custom-navigation');
25
26navigationLinks.forEach(link => {
27  link.addEventListener('click', function(event) {
28    event.preventDefault(); // BLOCK standard navigation
29
30    const targetUrl = this.getAttribute('href');
31    console.log('Link click intercepted:', targetUrl);
32
33    // Custom navigation logic (e.g., SPA routing)
34    navigateToPage(targetUrl);
35  });
36});
37
38function validateAccessCode(code) {
39  const validCodes = ['ALPHA-7', 'BETA-4', 'GAMMA-9'];
40  return validCodes.includes(code);
41}
42
43function navigateToPage(url) {
44  console.log('Navigating to:', url);
45  // SPA routing implementation
46  history.pushState({}, '', url);
47  loadPageContent(url);
48}

stopPropagation() - Stopping Propagation

1// HTML:
2// <div class="park-section" id="section-a">
3//   <div class="enclosure" id="enclosure-1">
4//     <button class="action-button">Action</button>
5//   </div>
6// </div>
7
8const parkSection = document.getElementById('section-a');
9const enclosure = document.getElementById('enclosure-1');
10const actionButton = document.querySelector('.action-button');
11
12// Listener on the park section
13parkSection.addEventListener('click', function(event) {
14  console.log('3. Park section clicked');
15  updateSectionStatus();
16});
17
18// Listener on the enclosure
19enclosure.addEventListener('click', function(event) {
20  console.log('2. Enclosure clicked');
21  event.stopPropagation(); // STOP! Don't propagate higher
22  highlightEnclosure();
23});
24
25// Listener on the button
26actionButton.addEventListener('click', function(event) {
27  console.log('1. Action button clicked');
28  performAction();
29  // The event will propagate to enclosure, but will be stopped there
30});
31
32// Without stopPropagation() in enclosure - we'd see all 3 logs
33// With stopPropagation() - we'll only see 1 and 2

stopImmediatePropagation() - Complete Stop

1const emergencyButton = document.getElementById('emergency');
2
3// First listener
4emergencyButton.addEventListener('click', function(event) {
5  console.log('1. First listener - alarm activation');
6  activateAlarm();
7
8  event.stopImmediatePropagation(); // STOP EVERYTHING!
9});
10
11// Second listener - WON'T execute!
12emergencyButton.addEventListener('click', function(event) {
13  console.log('2. This listener WON\'T execute');
14  closeGates();
15});
16
17// Third listener - WON'T execute!
18emergencyButton.addEventListener('click', function(event) {
19  console.log('3. This one WON\'T execute either');
20  notifyStaff();
21});
22
23// After clicking, we'll only see the first log

Practical Monitoring System

"Let's combine everything into a working system," says Ray, opening the main code.

1class DinosaurMonitoringSystem {
2  constructor() {
3    this.selectedDinosaurs = [];
4    this.monitoringActive = false;
5    this.initialize();
6  }
7
8  initialize() {
9    console.log('Initializing dinosaur monitoring system');
10    this.setupEventListeners();
11  }
12
13  setupEventListeners() {
14    const dinosaurCards = document.querySelectorAll('.dinosaur-card');
15
16    dinosaurCards.forEach(card => {
17      // Click event with full analysis
18      card.addEventListener('click', (event) => {
19        this.handleDinosaurClick(event);
20      });
21
22      // Context menu
23      card.addEventListener('contextmenu', (event) => {
24        event.preventDefault();
25        this.showDinosaurMenu(event);
26      });
27
28      // Hover effects
29      card.addEventListener('mouseenter', (event) => {
30        this.showQuickInfo(event);
31      });
32
33      card.addEventListener('mouseleave', (event) => {
34        this.hideQuickInfo(event);
35      });
36    });
37  }
38
39  handleDinosaurClick(event) {
40    const card = event.currentTarget;
41    const dinoId = card.dataset.dinoId;
42    const dinoName = card.dataset.dinoName;
43
44    console.log('=== CLICK REPORT ===');
45    console.log('Dinosaur:', dinoName);
46    console.log('Click position:', event.clientX, event.clientY);
47    console.log('Timestamp:', event.timeStamp);
48    console.log('Ctrl:', event.ctrlKey);
49    console.log('Shift:', event.shiftKey);
50    console.log('Alt:', event.altKey);
51    console.log('Mouse button:', event.button);
52
53    // Selection logic
54    if (event.ctrlKey) {
55      this.toggleSelection(dinoId, card);
56    } else if (event.shiftKey) {
57      this.selectRange(dinoId);
58    } else {
59      this.selectSingle(dinoId, card);
60    }
61  }
62
63  toggleSelection(dinoId, card) {
64    const index = this.selectedDinosaurs.indexOf(dinoId);
65
66    if (index === -1) {
67      this.selectedDinosaurs.push(dinoId);
68      card.classList.add('selected');
69      console.log(`Added ${dinoId} to selection`);
70    } else {
71      this.selectedDinosaurs.splice(index, 1);
72      card.classList.remove('selected');
73      console.log(`Removed ${dinoId} from selection`);
74    }
75
76    console.log('Selected dinosaurs:', this.selectedDinosaurs);
77  }
78
79  selectSingle(dinoId, card) {
80    // Clear previous selection
81    document.querySelectorAll('.dinosaur-card.selected').forEach(c => {
82      c.classList.remove('selected');
83    });
84
85    this.selectedDinosaurs = [dinoId];
86    card.classList.add('selected');
87
88    console.log('Selected single dinosaur:', dinoId);
89  }
90
91  showDinosaurMenu(event) {
92    const card = event.currentTarget;
93    const dinoId = card.dataset.dinoId;
94
95    console.log('Displaying context menu for:', dinoId);
96    console.log('Position:', event.clientX, event.clientY);
97
98    // Menu display logic would go here
99  }
100
101  showQuickInfo(event) {
102    const card = event.currentTarget;
103    const dinoName = card.dataset.dinoName;
104
105    console.log(`Hovered over: ${dinoName}`);
106    console.log('Mouse position:', event.clientX, event.clientY);
107  }
108
109  hideQuickInfo(event) {
110    console.log('Left dinosaur card');
111  }
112}
113
114// System initialization
115const monitoringSystem = new DinosaurMonitoringSystem();

Summary

"The Event object," Ray summarizes, "is your window into the world of user interaction. Every property, every method has its meaning and use. Just as every sensor reading in Jurassic Park helps us maintain safety, Event properties help you create precise, responsive applications."

Key properties:

  • event.target - the element that triggered the event
  • event.currentTarget - the element with the listener
  • event.type - the event type
  • event.clientX/Y, event.pageX/Y - cursor positions
  • event.ctrlKey, event.shiftKey, event.altKey - modifier keys
  • event.button - mouse button

Key methods:

  • event.preventDefault() - block the default action
  • event.stopPropagation() - stop propagation
  • event.stopImmediatePropagation() - stop all listeners
Code for this lesson: index.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>Visitor Registration - Jurassic Park</title>
7    <style>
8        body {
9            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10            background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
11            margin: 0;
12            padding: 20px;
13            color: white;
14        }
15
16        .registration-container {
17            max-width: 500px;
18            margin: 0 auto;
19            background: rgba(0, 0, 0, 0.8);
20            border-radius: 15px;
21            padding: 30px;
22            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
23        }
24
25        h1 {
26            text-align: center;
27            margin-bottom: 30px;
28            color: #ecf0f1;
29        }
30
31        .form-group {
32            margin-bottom: 20px;
33        }
34
35        label {
36            display: block;
37            margin-bottom: 5px;
38            color: #bdc3c7;
39            font-weight: 500;
40        }
41
42        input[type="text"],
43        input[type="email"],
44        input[type="number"],
45        input[type="date"],
46        select {
47            width: 100%;
48            padding: 12px;
49            border: 2px solid #34495e;
50            border-radius: 8px;
51            background: rgba(255, 255, 255, 0.1);
52            color: white;
53            font-size: 16px;
54            transition: all 0.3s ease;
55            box-sizing: border-box;
56        }
57
58        input[type="text"]:focus,
59        input[type="email"]:focus,
60        input[type="number"]:focus,
61        input[type="date"]:focus,
62        select:focus {
63            outline: none;
64            border-color: #3498db;
65            background: rgba(255, 255, 255, 0.15);
66        }
67
68        .input-valid {
69            border-color: #27ae60 !important;
70            background: rgba(39, 174, 96, 0.1) !important;
71        }
72
73        .input-invalid {
74            border-color: #e74c3c !important;
75            background: rgba(231, 76, 60, 0.1) !important;
76        }
77
78        .error-message {
79            color: #e74c3c;
80            font-size: 14px;
81            margin-top: 5px;
82            display: none;
83        }
84
85        .submit-btn {
86            width: 100%;
87            padding: 15px;
88            background: linear-gradient(45deg, #27ae60, #2ecc71);
89            color: white;
90            border: none;
91            border-radius: 8px;
92            font-size: 18px;
93            font-weight: bold;
94            cursor: pointer;
95            transition: all 0.3s ease;
96            margin-top: 20px;
97        }
98
99        .submit-btn:hover {
100            transform: translateY(-2px);
101            box-shadow: 0 10px 25px rgba(39, 174, 96, 0.3);
102        }
103
104        .submit-btn:disabled {
105            background: #95a5a6;
106            cursor: not-allowed;
107            transform: none;
108            box-shadow: none;
109        }
110
111        .success-message {
112            background: #27ae60;
113            color: white;
114            padding: 15px;
115            border-radius: 8px;
116            margin-top: 20px;
117            text-align: center;
118            display: none;
119        }
120
121        .form-info {
122            background: rgba(52, 152, 219, 0.2);
123            border: 1px solid #3498db;
124            border-radius: 8px;
125            padding: 15px;
126            margin-bottom: 20px;
127        }
128
129        .required {
130            color: #e74c3c;
131        }
132    </style>
133</head>
134<body>
135    <div class="registration-container">
136        <h1>Visitor Registration</h1>
137
138        <div class="form-info">
139            Fill out the form to register for a Jurassic Park tour.
140            Fields marked with <span class="required">*</span> are required.
141        </div>
142
143        <form id="visitor-form">
144            <div class="form-group">
145                <label for="name">Full name <span class="required">*</span></label>
146                <input type="text" id="name" name="name" required>
147                <div class="error-message" id="name-error">Full name must have at least 2 characters</div>
148            </div>
149
150            <div class="form-group">
151                <label for="email">Email address <span class="required">*</span></label>
152                <input type="email" id="email" name="email" required>
153                <div class="error-message" id="email-error">Enter a valid email address</div>
154            </div>
155
156            <div class="form-group">
157                <label for="age">Age <span class="required">*</span></label>
158                <input type="number" id="age" name="age" min="5" max="120" required>
159                <div class="error-message" id="age-error">Age must be between 5 and 120 years</div>
160            </div>
161
162            <div class="form-group">
163                <label for="phone">Phone number</label>
164                <input type="text" id="phone" name="phone" placeholder="+48 123 456 789">
165                <div class="error-message" id="phone-error">Enter a valid phone number</div>
166            </div>
167
168            <div class="form-group">
169                <label for="visit-date">Visit date <span class="required">*</span></label>
170                <input type="date" id="visit-date" name="visit-date" required>
171                <div class="error-message" id="date-error">Visit date must be in the future</div>
172            </div>
173
174            <div class="form-group">
175                <label for="tour-type">Tour type <span class="required">*</span></label>
176                <select id="tour-type" name="tour-type" required>
177                    <option value="">Choose a tour type</option>
178                    <option value="standard">Standard (2h)</option>
179                    <option value="vip">VIP with a guide (4h)</option>
180                    <option value="night">Night tour (3h)</option>
181                    <option value="research">Research tour (6h)</option>
182                </select>
183                <div class="error-message" id="tour-error">Choose a tour type</div>
184            </div>
185
186            <button type="submit" id="submit-btn" class="submit-btn" disabled>
187                Register for the tour
188            </button>
189        </form>
190
191        <div id="success-message" class="success-message">
192            Registration completed successfully! Confirmation has been sent to the provided email address.
193        </div>
194    </div>
195
196    <script>
197        // Get references to the form elements
198        const form = document.getElementById('visitor-form');
199        const submitBtn = document.getElementById('submit-btn');
200        const successMessage = document.getElementById('success-message');
201
202        // Validation functions
203        function validateName(name) {
204            return name.length >= 2;
205        }
206
207        function validateEmail(email) {
208            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
209            return emailRegex.test(email);
210        }
211
212        function validateAge(age) {
213            const ageNum = parseInt(age);
214            return ageNum >= 5 && ageNum <= 120;
215        }
216
217        function validatePhone(phone) {
218            if (phone === '') return true; // Optional field
219            const phoneRegex = /^(\+\d{1,3}\s?)?\d{3}\s?\d{3}\s?\d{3}$/;
220            return phoneRegex.test(phone.replace(/\s/g, ''));
221        }
222
223        function validateDate(date) {
224            const selectedDate = new Date(date);
225            const today = new Date();
226            today.setHours(0, 0, 0, 0);
227            return selectedDate > today;
228        }
229
230        function validateTourType(tourType) {
231            return tourType !== '';
232        }
233
234        // Function to show/hide errors
235        function showError(fieldId, show) {
236            const errorElement = document.getElementById(fieldId + '-error');
237            const inputElement = document.getElementById({ date: 'visit-date', tour: 'tour-type' }[fieldId] || fieldId);
238
239            if (show) {
240                errorElement.style.display = 'block';
241                inputElement.classList.add('input-invalid');
242                inputElement.classList.remove('input-valid');
243            } else {
244                errorElement.style.display = 'none';
245                inputElement.classList.remove('input-invalid');
246                inputElement.classList.add('input-valid');
247            }
248        }
249
250        // Function checking if form is valid
251        function checkFormValidity() {
252            const name = document.getElementById('name').value;
253            const email = document.getElementById('email').value;
254            const age = document.getElementById('age').value;
255            const phone = document.getElementById('phone').value;
256            const visitDate = document.getElementById('visit-date').value;
257            const tourType = document.getElementById('tour-type').value;
258
259            const isValid = validateName(name) &&
260                           validateEmail(email) &&
261                           validateAge(age) &&
262                           validatePhone(phone) &&
263                           validateDate(visitDate) &&
264                           validateTourType(tourType);
265
266            submitBtn.disabled = !isValid;
267            return isValid;
268        }
269
270        // Event listeners for real-time validation
271        document.getElementById('name').addEventListener('input', function(e) {
272            const isValid = validateName(e.target.value);
273            showError('name', !isValid);
274            checkFormValidity();
275        });
276
277        document.getElementById('email').addEventListener('input', function(e) {
278            const isValid = validateEmail(e.target.value);
279            showError('email', !isValid);
280            checkFormValidity();
281        });
282
283        document.getElementById('age').addEventListener('input', function(e) {
284            const isValid = validateAge(e.target.value);
285            showError('age', !isValid);
286            checkFormValidity();
287        });
288
289        document.getElementById('phone').addEventListener('input', function(e) {
290            const isValid = validatePhone(e.target.value);
291            showError('phone', !isValid);
292            checkFormValidity();
293        });
294
295        document.getElementById('visit-date').addEventListener('input', function(e) {
296            const isValid = validateDate(e.target.value);
297            showError('date', !isValid);
298            checkFormValidity();
299        });
300
301        document.getElementById('tour-type').addEventListener('change', function(e) {
302            const isValid = validateTourType(e.target.value);
303            showError('tour', !isValid);
304            checkFormValidity();
305        });
306
307        // Handle form submission
308        form.addEventListener('submit', function(e) {
309            e.preventDefault(); // Prevent default form submission
310
311            // Check validity once more
312            if (!checkFormValidity()) {
313                alert('Form contains errors. Fix them before submitting.');
314                return;
315            }
316
317            // Collecting the form data
318            const formData = new FormData(form);
319            const visitorData = {
320                name: formData.get('name'),
321                email: formData.get('email'),
322                age: parseInt(formData.get('age')),
323                phone: formData.get('phone') || 'Not provided',
324                visitDate: formData.get('visit-date'),
325                tourType: formData.get('tour-type')
326            };
327
328            console.log('Visitor data:', visitorData);
329
330            // Simulate sending data
331            submitBtn.textContent = 'Registering...';
332            submitBtn.disabled = true;
333
334            setTimeout(() => {
335                // Hiding the form and showing the success message
336                form.style.display = 'none';
337                successMessage.style.display = 'block';
338
339                console.log('Registration completed successfully!');
340
341                // After 3 seconds we show the form again
342                setTimeout(() => {
343                    form.style.display = 'block';
344                    successMessage.style.display = 'none';
345                    form.reset();
346                    submitBtn.textContent = 'Register for the tour';
347                    checkFormValidity();
348
349                    // Reset field styles
350                    document.querySelectorAll('input, select').forEach(input => {
351                        input.classList.remove('input-valid', 'input-invalid');
352                    });
353
354                    document.querySelectorAll('.error-message').forEach(error => {
355                        error.style.display = 'none';
356                    });
357                }, 3000);
358            }, 2000);
359        });
360
361        // Set minimum date to tomorrow
362        const tomorrow = new Date();
363        tomorrow.setDate(tomorrow.getDate() + 1);
364        document.getElementById('visit-date').min = tomorrow.toISOString().split('T')[0];
365    </script>
366</body>
367</html>

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. Which property of the event object contains the element that triggered the event?

  2. 2. What is the event.preventDefault() method used for?

Hands-on tasks in the game

  • Code editor

    The index.html file has a visitor registration form (fields name and email) and a script in a <script> tag. Fill in the blanks: ___BLANK1___ is the form submission event, ___BLANK2___ is the event method that stops the default submission and page reload, and ___BLANK3___ is a condition that is true when the e-mail does not contain an @ sign. Valid data should show a greeting with the name, invalid data an error message.

  • Horizontal ordering

    Arrange the elements for preventing the default action in an event handler:

Useful articles