Ray Arnold prowadzi Cię głębiej do centrum kontrolnego. "To tutaj wszystko się zaczyna," wskazuje na ogromny panel z tysiącami przycisków i wskaźników. "Każdy element tego systemu jest event listenerem - nasłuchiwaczem oczekującym na sygnał."
"W Parku Jurajskim," kontynuuje, "mamy sensory ruchu, czujniki temperatury, kamery, mikrofony. Każdy z nich ciągle słucha, czeka na coś nietypowego. Gdy to wykryje, natychmiast reaguje. W JavaScript mamy
addEventListener() - metodę która działa dokładnie tak samo."Metoda
addEventListener() to fundament obsługi wydarzeń w nowoczesnym JavaScript. Pozwala przypisać funkcję obsługującą do konkretnego typu wydarzenia na wybranym elemencie.1element.addEventListener(eventType, handlerFunction, options);"Zacznijmy od czegoś prostego," mówi Ray. "Przycisk karmienia Velociraptorów."
1// Pobierz element przycisku
2const feedButton = document.getElementById('velociraptor-feed');
3
4// Zarejestruj listener
5feedButton.addEventListener('click', function(event) {
6 console.log('Rozpoczynam procedurę karmienia Velociraptorów');
7 console.log('Kliknięto w:', event.target);
8
9 // Logika karmienia
10 feedVelociraptors();
11});
12
13function feedVelociraptors() {
14 console.log('Klatka bezpieczeństwa: ZAMKNIĘTA');
15 console.log('Podajnik pokarmu: AKTYWNY');
16 console.log('Monitoring: WŁĄCZONY');
17 console.log('Procedura zakończona pomyślnie');
18}"Co jeśli potrzebujemy wielu reakcji na to samo wydarzenie?" pyta Ray. "W systemach parku to normalne - jedno wydarzenie może uruchamiać wiele protokołów."
1const emergencyButton = document.getElementById('emergency');
2
3// Pierwszy listener - aktywacja alarmu
4emergencyButton.addEventListener('click', function() {
5 console.log('ALARM: Aktywuję syreny alarmowe');
6 activateSirens();
7});
8
9// Drugi listener - zamykanie bram
10emergencyButton.addEventListener('click', function() {
11 console.log('BEZPIECZEŃSTWO: Zamykam wszystkie bramy');
12 lockAllGates();
13});
14
15// Trzeci listener - powiadamianie personelu
16emergencyButton.addEventListener('click', function() {
17 console.log('KOMUNIKACJA: Wysyłam powiadomienia do zespołu');
18 notifySecurityTeam();
19});
20
21// Wszystkie trzy funkcje wykonają się po kliknięciuRay pokazuje Ci różne sposoby definiowania handlerów:
1const enclosureGate = document.getElementById('gate-5');
2
3// 1. Funkcja anonimowa
4enclosureGate.addEventListener('click', function(event) {
5 console.log('Kliknięto bramę', event.target.id);
6});
7
8// 2. Arrow function (nowoczesny JavaScript)
9enclosureGate.addEventListener('click', (event) => {
10 console.log('Kliknięto bramę', event.target.id);
11});
12
13// 3. Named function (najlepsza praktyka)
14function handleGateClick(event) {
15 console.log('Kliknięto bramę', event.target.id);
16
17 if (event.target.dataset.status === 'open') {
18 closeGate(event.target.id);
19 } else {
20 openGate(event.target.id);
21 }
22}
23
24enclosureGate.addEventListener('click', handleGateClick);"Named functions są najlepsze," tłumaczy Ray, "bo możesz je później usunąć, testować osobno, i kod jest bardziej czytelny."
"Gdy sensor wykryje ruch," Ray pokazuje raport na ekranie, "dostajemy pełny pakiet danych: gdzie, kiedy, jaki typ ruchu, jak szybki. Event object w JavaScript działa tak samo."
1document.addEventListener('click', function(event) {
2 // Podstawowe informacje o wydarzeniu
3 console.log('Typ wydarzenia:', event.type); // 'click'
4 console.log('Timestamp:', event.timeStamp); // czas w ms od załadowania strony
5
6 // Element, który był celem wydarzenia
7 console.log('Element docelowy:', event.target);
8 console.log('Bieżący element:', event.currentTarget);
9
10 // Informacje o pozycji (dla wydarzeń myszy)
11 console.log('Pozycja na ekranie - X:', event.screenX, 'Y:', event.screenY);
12 console.log('Pozycja w oknie - X:', event.clientX, 'Y:', event.clientY);
13 console.log('Pozycja względem strony - X:', event.pageX, 'Y:', event.pageY);
14
15 // Klawisze modyfikujące
16 console.log('Shift:', event.shiftKey);
17 console.log('Ctrl:', event.ctrlKey);
18 console.log('Alt:', event.altKey);
19 console.log('Meta (Cmd/Win):', event.metaKey);
20
21 // Przycisk myszy
22 console.log('Przycisk:', event.button); // 0=lewy, 1=środek, 2=prawy
23});"Zobaczmy to w realnym systemie," mówi Ray, otwierając kod interaktywnej mapy parku.
1const parkMap = document.getElementById('interactive-park-map');
2
3parkMap.addEventListener('click', function(event) {
4 // Pobierz współrzędne kliknięcia
5 const x = event.clientX;
6 const y = event.clientY;
7
8 // Sprawdź czy kliknięto z Ctrl (wielokrotny wybór)
9 if (event.ctrlKey) {
10 console.log('Wybór wielokrotny - dodaję do selekcji');
11 addToSelection(event.target);
12 } else {
13 console.log('Wybór pojedynczy - czyszczę poprzednią selekcję');
14 clearSelection();
15 selectSingle(event.target);
16 }
17
18 // Różne akcje dla różnych przycisków myszy
19 switch(event.button) {
20 case 0: // Lewy przycisk
21 showEnclosureDetails(event.target);
22 break;
23 case 2: // Prawy przycisk
24 showContextMenu(x, y);
25 event.preventDefault(); // Zablokuj domyślne menu kontekstowe
26 break;
27 }
28
29 // Zapisz pozycję dla późniejszego użycia
30 lastClickPosition = { x, y };
31});
32
33// Blokowanie menu kontekstowego (prawy przycisk)
34parkMap.addEventListener('contextmenu', function(event) {
35 event.preventDefault();
36 console.log('Własne menu kontekstowe zamiast domyślnego');
37});Ray pokazuje kolejne ekrany z różnymi systemami. "Każdy typ sensora wykrywa co innego. Podobnie mamy różne typy wydarzeń."
1const dinoCard = document.getElementById('trex-card');
2
3// Kliknięcia
4dinoCard.addEventListener('click', (e) => {
5 console.log('Pojedyncze kliknięcie');
6 showBasicInfo();
7});
8
9dinoCard.addEventListener('dblclick', (e) => {
10 console.log('Podwójne kliknięcie');
11 showDetailedView();
12});
13
14// Najechanie i opuszczenie
15dinoCard.addEventListener('mouseenter', (e) => {
16 console.log('Mysz weszła na kartę');
17 e.target.classList.add('highlighted');
18 showQuickPreview();
19});
20
21dinoCard.addEventListener('mouseleave', (e) => {
22 console.log('Mysz opuściła kartę');
23 e.target.classList.remove('highlighted');
24 hideQuickPreview();
25});
26
27// Ruch myszy (używaj ostrożnie - może generować setki wydarzeń)
28dinoCard.addEventListener('mousemove', (e) => {
29 // Aktualizuj pozycję tooltipa
30 updateTooltipPosition(e.clientX, e.clientY);
31});
32
33// Naciśnięcie i puszczenie przycisku
34dinoCard.addEventListener('mousedown', (e) => {
35 console.log('Przycisk myszy wciśnięty');
36 startDrag(e);
37});
38
39dinoCard.addEventListener('mouseup', (e) => {
40 console.log('Przycisk myszy puszczony');
41 endDrag(e);
42});"Wiele systemów sterujemy z klawiatury," wyjaśnia Ray. "Kody dostępu, skróty klawiszowe, nawigacja."
1// Nasłuchuj na całym dokumencie
2document.addEventListener('keydown', function(event) {
3 console.log('Wciśnięto klawisz:', event.key);
4 console.log('Kod klawisza:', event.code);
5 console.log('Czy to powtórzenie:', event.repeat);
6
7 // Skróty klawiszowe
8 if (event.ctrlKey && event.key === 's') {
9 event.preventDefault(); // Zablokuj domyślny zapis przeglądarki
10 saveParkConfiguration();
11 console.log('Konfiguracja zapisana');
12 }
13
14 if (event.key === 'Escape') {
15 closeAllModals();
16 console.log('Zamknięto wszystkie okna');
17 }
18
19 // Nawigacja strzałkami
20 switch(event.key) {
21 case 'ArrowUp':
22 moveCamera('north');
23 break;
24 case 'ArrowDown':
25 moveCamera('south');
26 break;
27 case 'ArrowLeft':
28 moveCamera('west');
29 break;
30 case 'ArrowRight':
31 moveCamera('east');
32 break;
33 }
34});
35
36// Nasłuchuj na konkretnym polu
37const securityCodeInput = document.getElementById('security-code');
38
39securityCodeInput.addEventListener('keypress', function(event) {
40 // Tylko cyfry
41 if (event.key < '0' || event.key > '9') {
42 event.preventDefault();
43 console.log('Tylko cyfry są dozwolone');
44 }
45});"Formularze to krytyczna część systemów parku," mówi Ray. "Raportowanie incydentów, rejestracja gości, monitoring."
1const incidentForm = document.getElementById('incident-report');
2const severitySelect = document.getElementById('severity');
3const descriptionArea = document.getElementById('description');
4
5// Submit - wysłanie formularza
6incidentForm.addEventListener('submit', function(event) {
7 event.preventDefault(); // WAŻNE: zablokuj domyślne wysłanie
8
9 console.log('Formularz wysłany');
10
11 // Zbierz dane
12 const formData = new FormData(incidentForm);
13 const data = {
14 severity: formData.get('severity'),
15 location: formData.get('location'),
16 description: formData.get('description'),
17 reporter: formData.get('reporter'),
18 timestamp: new Date().toISOString()
19 };
20
21 // Walidacja
22 if (validateIncidentReport(data)) {
23 submitIncidentReport(data);
24 incidentForm.reset();
25 } else {
26 console.error('Nieprawidłowe dane formularza');
27 }
28});
29
30// Change - zmiana wartości (po opuszczeniu pola)
31severitySelect.addEventListener('change', function(event) {
32 const severity = event.target.value;
33 console.log('Zmieniono poziom zagrożenia na:', severity);
34
35 // Dostosuj priorytet i kolor
36 if (severity === 'critical') {
37 incidentForm.classList.add('critical');
38 setPriority('HIGH');
39 } else {
40 incidentForm.classList.remove('critical');
41 setPriority('NORMAL');
42 }
43});
44
45// Input - zmiana wartości (na bieżąco)
46descriptionArea.addEventListener('input', function(event) {
47 const length = event.target.value.length;
48 const maxLength = 500;
49
50 console.log(`Znaków: ${length}/${maxLength}`);
51 updateCharacterCounter(length, maxLength);
52
53 if (length > maxLength) {
54 event.target.classList.add('error');
55 } else {
56 event.target.classList.remove('error');
57 }
58});
59
60// Focus i Blur - aktywacja i dezaktywacja pola
61descriptionArea.addEventListener('focus', function(event) {
62 console.log('Pole opisal aktywne - pokazuję pomocnicze wskazówki');
63 showHelpTips();
64});
65
66descriptionArea.addEventListener('blur', function(event) {
67 console.log('Pole opisu nieaktywne - ukrywam wskazówki');
68 hideHelpTips();
69});"Systemy które już nie są potrzebne," Ray pokazuje wyłączony panel, "należy wyłączyć. Oszczędza energię i zasoby."
1// Funkcja musi być named, żeby móc ją usunąć
2function handleClick(event) {
3 console.log('Kliknięto');
4}
5
6const button = document.getElementById('temp-button');
7
8// Dodaj listener
9button.addEventListener('click', handleClick);
10
11// Później usuń (musi być ta sama funkcja)
12button.removeEventListener('click', handleClick);
13
14// ❌ To NIE ZADZIAŁA - to dwie różne funkcje
15button.addEventListener('click', () => console.log('Test'));
16button.removeEventListener('click', () => console.log('Test'));1function setupMaintenanceMode() {
2 console.log('Włączanie trybu konserwacji na 5 minut');
3
4 const maintenanceAlert = function(event) {
5 event.preventDefault();
6 alert('Park jest w trybie konserwacji. Spróbuj za chwilę.');
7 };
8
9 // Zablokuj wszystkie akcje
10 document.addEventListener('click', maintenanceAlert, true); // true = capture
11
12 // Po 5 minutach wyłącz tryb konserwacji
13 setTimeout(() => {
14 document.removeEventListener('click', maintenanceAlert, true);
15 console.log('Tryb konserwacji wyłączony');
16 }, 5 * 60 * 1000);
17}Ray pokazuje zaawansowany panel konfiguracji. "Czasami potrzebujemy specjalnych ustawień dla sensorów."
1element.addEventListener(eventType, handler, {
2 capture: false, // Użyj fazy capture zamiast bubbling
3 once: false, // Usuń listener po pierwszym wywołaniu
4 passive: false // Nie wywołuj preventDefault()
5});1const welcomeScreen = document.getElementById('welcome');
2
3// Pokaż wita</div>cz tylko raz
4welcomeScreen.addEventListener('click', function() {
5 console.log('Witamy w Parku Jurajskim!');
6 showTour();
7}, { once: true });
8
9// Po pierwszym kliknięciu listener jest automatycznie usuwany1// Dla wydajności przy scroll
2window.addEventListener('scroll', function(event) {
3 updateScrollPosition();
4 // Nie możemy użyć event.preventDefault() z passive: true
5}, { passive: true });"Połączmy wszystko w jeden system," mówi Ray, otwierając główny kod monitoringu.
1class ParkMonitoringSystem {
2 constructor() {
3 this.alerts = [];
4 this.isActive = false;
5 this.initialize();
6 }
7
8 initialize() {
9 console.log('Inicjalizacja systemu monitoringu...');
10
11 // Wydarzenia dokumentu
12 document.addEventListener('DOMContentLoaded', () => {
13 this.onSystemReady();
14 });
15
16 // Wydarzenia okna
17 window.addEventListener('beforeunload', (e) => {
18 return this.onBeforeExit(e);
19 });
20
21 // Wydarzenia errorów
22 window.addEventListener('error', (e) => {
23 this.logError(e);
24 });
25
26 this.isActive = true;
27 }
28
29 onSystemReady() {
30 console.log('System monitoringu gotowy');
31 this.setupEnclosureMonitors();
32 this.setupSecuritySystem();
33 this.startPeriodicChecks();
34 }
35
36 setupEnclosureMonitors() {
37 const enclosures = document.querySelectorAll('.enclosure');
38
39 enclosures.forEach(enclosure => {
40 // Status zagrody
41 enclosure.addEventListener('click', (e) => {
42 this.showEnclosureStatus(enclosure.id);
43 });
44
45 // Szczegóły po podwójnym kliknięciu
46 enclosure.addEventListener('dblclick', (e) => {
47 this.showDetailedView(enclosure.id);
48 });
49
50 // Highlight przy najechaniu
51 enclosure.addEventListener('mouseenter', (e) => {
52 e.target.classList.add('active');
53 this.showQuickInfo(enclosure.id);
54 });
55
56 enclosure.addEventListener('mouseleave', (e) => {
57 e.target.classList.remove('active');
58 this.hideQuickInfo();
59 });
60 });
61 }
62
63 setupSecuritySystem() {
64 // Skróty klawiszowe
65 document.addEventListener('keydown', (e) => {
66 if (e.ctrlKey && e.shiftKey && e.key === 'E') {
67 this.triggerEmergency();
68 }
69
70 if (e.key === 'F1') {
71 e.preventDefault();
72 this.showHelp();
73 }
74 });
75
76 // Przycisk awaryjny
77 const emergencyBtn = document.getElementById('emergency');
78 if (emergencyBtn) {
79 emergencyBtn.addEventListener('click', () => {
80 this.triggerEmergency();
81 });
82 }
83 }
84
85 startPeriodicChecks() {
86 // Co 30 sekund sprawdź status wszystkich systemów
87 this.checkInterval = setInterval(() => {
88 if (this.isActive) {
89 this.performSystemCheck();
90 }
91 }, 30000);
92 }
93
94 performSystemCheck() {
95 console.log(`[${new Date().toLocaleTimeString()}] Sprawdzanie systemów...`);
96 // Logika sprawdzania
97 }
98
99 triggerEmergency() {
100 console.error('🚨 ALARM AWARYJNY!');
101 this.addAlert({
102 type: 'emergency',
103 message: 'Alarm awaryjny aktywowany',
104 timestamp: new Date()
105 });
106 }
107
108 addAlert(alert) {
109 this.alerts.push(alert);
110 const event = new CustomEvent('parkAlert', { detail: alert });
111 document.dispatchEvent(event);
112 }
113
114 onBeforeExit(event) {
115 if (this.alerts.some(a => a.type === 'emergency')) {
116 event.returnValue = 'Są aktywne alerty! Czy na pewno chcesz wyjść?';
117 return event.returnValue;
118 }
119 }
120
121 logError(event) {
122 console.error('Błąd systemu:', event.error);
123 this.addAlert({
124 type: 'error',
125 message: event.error.message,
126 timestamp: new Date()
127 });
128 }
129
130 destroy() {
131 // Wyłącz monitoring
132 this.isActive = false;
133 clearInterval(this.checkInterval);
134 console.log('System monitoringu wyłączony');
135 }
136}
137
138// Inicjalizacja
139const parkMonitoring = new ParkMonitoringSystem();"Event listenery to rdzeń każdego interaktywnego systemu," podsumowuje Ray. "Podobnie jak sensory w Parku Jurajskim czuwają nad bezpieczeństwem, tak event listenery czuwają nad funkcjonalnością Twojej aplikacji."
Kluczowe zasady:
addEventListener() dla wszystkich listenerówremoveEventListener() gdy listener nie jest już potrzebny{ once: true } dla jednorazowych operacji{ passive: true } dla scroll/touch dla wydajności