JavaScript and TypeScript course Β· Module 8: Events and the DOM
The Event Object and Its Properties
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 2stopImmediatePropagation() - 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 logPractical 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 eventevent.currentTarget- the element with the listenerevent.type- the event typeevent.clientX/Y,event.pageX/Y- cursor positionsevent.ctrlKey,event.shiftKey,event.altKey- modifier keysevent.button- mouse button
Key methods:
event.preventDefault()- block the default actionevent.stopPropagation()- stop propagationevent.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. Which property of the event object contains the element that triggered the event?
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: