Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Event Listeners - nasłuchiwanie na wydarzenia

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."

addEventListener() - podstawowa metoda

Metoda

addEventListener()
to fundament obsługi wydarzeń w nowoczesnym JavaScript. Pozwala przypisać funkcję obsługującą do konkretnego typu wydarzenia na wybranym elemencie.

Składnia

1element.addEventListener(eventType, handlerFunction, options);
  • eventType - typ wydarzenia (string): 'click', 'keydown', 'submit' etc.
  • handlerFunction - funkcja wywoływana gdy wydarzenie wystąpi
  • options - opcjonalne parametry (o nich później)

Podstawowy przykład

"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}

Wiele listenerów na tym samym elemencie

"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ęciu

Różne typy funkcji obsługujących

Ray 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."

Obiekt Event - pełen raport z wydarzenia

"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."

Podstawowe właściwości

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});

Praktyczne wykorzystanie właściwości

"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});

Różne typy wydarzeń

Ray pokazuje kolejne ekrany z różnymi systemami. "Każdy typ sensora wykrywa co innego. Podobnie mamy różne typy wydarzeń."

Wydarzenia myszy

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});

Wydarzenia klawiatury

"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});

Wydarzenia formularzy

"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});

removeEventListener() - usuwanie listenerów

"Systemy które już nie są potrzebne," Ray pokazuje wyłączony panel, "należy wyłączyć. Oszczędza energię i zasoby."

Podstawowe usuwanie

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'));

Praktyczny przykład - czasowy monitoring

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}

Opcje addEventListener()

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});

Przykład: once - jednorazowy listener

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 usuwany

Przykład: passive - lepszy scroll

1// 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 });

System monitoring w praktyce

"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();

Podsumowanie

"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:

  • Używaj
    addEventListener()
    dla wszystkich listenerów
  • Zawsze sprawdzaj czy element istnieje przed dodaniem listenera
  • Używaj named functions dla listenerów które musisz usunąć
  • Pamiętaj o
    removeEventListener()
    gdy listener nie jest już potrzebny
  • Używaj opcji
    { once: true }
    dla jednorazowych operacji
  • Używaj
    { passive: true }
    dla scroll/touch dla wydajności
Przejdź do CodeWorlds