Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Wydarzenia myszy i klawiatury

"W systemach bezpieczeństwa Parku Jurajskiego," Ray Arnold pokazuje wielki panel kontrolny, "mamy różne typy sensorów. Czujniki ruchu reagują na ruch, czujniki ciśnienia na dotyk, czujniki dźwięku na hałas. Każdy typ wymaga innej reakcji. W JavaScript mamy podobną sytuację - różne typy wejścia wymagają różnej obsługi."

Ray wskazuje na monitor i klawiaturę. "Mysz i klawiatura to dwa podstawowe źródła interakcji użytkownika. Każde ma swoje unikalne wydarzenia i zachowania."

Wydarzenia myszy - pełny zakres interakcji

Mysz generuje dziesiątki różnych typów wydarzeń, od prostych kliknięć po zaawansowane gesty przeciągania.

Podstawowe wydarzenia kliknięć

1const dinosaurViewingArea = document.getElementById('viewing-area');
2
3// click - pojedyncze kliknięcie (najpopularniejsze)
4dinosaurViewingArea.addEventListener('click', (event) => {
5  console.log('Pojedyncze kliknięcie w obszar obserwacyjny');
6  console.log('Pozycja:', event.clientX, event.clientY);
7
8  selectDinosaurAtPosition(event.clientX, event.clientY);
9});
10
11// dblclick - podwójne kliknięcie
12dinosaurViewingArea.addEventListener('dblclick', (event) => {
13  console.log('Podwójne kliknięcie - zbliżenie');
14
15  // Prevent text selection na double click
16  event.preventDefault();
17
18  zoomInOnDinosaur(event.clientX, event.clientY);
19});
20
21// auxclick - kliknięcie środkowym/innym przyciskiem (nie lewy)
22dinosaurViewingArea.addEventListener('auxclick', (event) => {
23  if (event.button === 1) {
24    console.log('Środkowy przycisk - otwieranie w nowym oknie');
25    openInNewWindow(event.target);
26  }
27});
28
29// contextmenu - prawy przycisk myszy (menu kontekstowe)
30dinosaurViewingArea.addEventListener('contextmenu', (event) => {
31  event.preventDefault(); // Zablokuj domyślne menu
32
33  console.log('Prawy przycisk - własne menu kontekstowe');
34  showCustomContextMenu(event.clientX, event.clientY);
35});
36
37function selectDinosaurAtPosition(x, y) {
38  console.log(`Sprawdzanie czy dinozaur znajduje się na pozycji (${x}, ${y})`);
39  // Logika selekcji
40}
41
42function zoomInOnDinosaur(x, y) {
43  console.log(`Zbliżenie na pozycję (${x}, ${y})`);
44  // Logika zoom
45}
46
47function showCustomContextMenu(x, y) {
48  const menu = document.getElementById('custom-menu');
49  menu.style.left = x + 'px';
50  menu.style.top = y + 'px';
51  menu.style.display = 'block';
52
53  console.log('Menu kontekstowe wyświetlone');
54}

Wydarzenia przycisków myszy

1const controlPanel = document.getElementById('control-panel');
2
3// mousedown - naciśnięcie przycisku myszy (przed puszczeniem)
4controlPanel.addEventListener('mousedown', (event) => {
5  console.log('Przycisk myszy wciśnięty');
6  console.log('Który przycisk:', event.button); // 0=lewy, 1=środek, 2=prawy
7
8  if (event.button === 0) {
9    console.log('Lewy przycisk - rozpoczynam przeciąganie');
10    startDragging(event);
11  }
12});
13
14// mouseup - puszczenie przycisku myszy
15controlPanel.addEventListener('mouseup', (event) => {
16  console.log('Przycisk myszy puszczony');
17
18  if (isDragging) {
19    console.log('Kończę przeciąganie');
20    stopDragging(event);
21  }
22});
23
24let isDragging = false;
25let dragStartX = 0;
26let dragStartY = 0;
27
28function startDragging(event) {
29  isDragging = true;
30  dragStartX = event.clientX;
31  dragStartY = event.clientY;
32
33  console.log(`Rozpoczęcie przeciągania z pozycji (${dragStartX}, ${dragStartY})`);
34}
35
36function stopDragging(event) {
37  const deltaX = event.clientX - dragStartX;
38  const deltaY = event.clientY - dragStartY;
39
40  console.log(`Przeciągnięto o: X=${deltaX}px, Y=${deltaY}px`);
41
42  isDragging = false;
43}

Wydarzenia ruchu myszy

1const parkMap = document.getElementById('interactive-park-map');
2
3// mousemove - ruch myszy (UWAGA: bardzo częste!)
4// Używaj z rozwagą - może generować setki eventów na sekundę
5
6let mouseMoveCount = 0;
7
8parkMap.addEventListener('mousemove', (event) => {
9  mouseMoveCount++;
10
11  // Aktualizuj pozycję kursora
12  updateCursorPosition(event.clientX, event.clientY);
13
14  // Jeśli przeciągamy, aktualizuj pozycję
15  if (isDragging) {
16    updateDragPosition(event.clientX, event.clientY);
17  }
18
19  // Log co 10-te wydarzenie (żeby nie zaśmiecać konsoli)
20  if (mouseMoveCount % 10 === 0) {
21    console.log(`Ruch myszy #${mouseMoveCount}: (${event.clientX}, ${event.clientY})`);
22  }
23});
24
25function updateCursorPosition(x, y) {
26  const coordsDisplay = document.getElementById('mouse-coords');
27  if (coordsDisplay) {
28    coordsDisplay.textContent = `X: ${x}, Y: ${y}`;
29  }
30}
31
32function updateDragPosition(x, y) {
33  const draggableElement = document.getElementById('draggable');
34  if (draggableElement) {
35    draggableElement.style.left = x + 'px';
36    draggableElement.style.top = y + 'px';
37  }
38}
39
40// mouseenter - mysz wchodzi w obszar elementu (nie bąbelkuje!)
41parkMap.addEventListener('mouseenter', (event) => {
42  console.log('Mysz weszła na mapę parku');
43  parkMap.classList.add('mouse-active');
44  showMapControls();
45});
46
47// mouseleave - mysz opuszcza obszar elementu (nie bąbelkuje!)
48parkMap.addEventListener('mouseleave', (event) => {
49  console.log('Mysz opuściła mapę parku');
50  parkMap.classList.remove('mouse-active');
51  hideMapControls();
52});
53
54// mouseover - mysz wchodzi (bąbelkuje - uruchamia się też dla dzieci!)
55parkMap.addEventListener('mouseover', (event) => {
56  if (event.target.classList.contains('enclosure-marker')) {
57    const enclosureName = event.target.dataset.name;
58    console.log(`Najechano na marker zagrody: ${enclosureName}`);
59    showEnclosureTooltip(event.target, enclosureName);
60  }
61});
62
63// mouseout - mysz wychodzi (bąbelkuje)
64parkMap.addEventListener('mouseout', (event) => {
65  if (event.target.classList.contains('enclosure-marker')) {
66    console.log('Opuszczono marker zagrody');
67    hideEnclosureTooltip();
68  }
69});
70
71function showMapControls() {
72  const controls = document.getElementById('map-controls');
73  if (controls) {
74    controls.style.display = 'block';
75  }
76}
77
78function hideMapControls() {
79  const controls = document.getElementById('map-controls');
80  if (controls) {
81    controls.style.display = 'none';
82  }
83}

Praktyczny przykład - system przeciągania

"Jedne z najbardziej użytecznych funkcji," mówi Ray, "to drag & drop."

1class DinosaurCardDragSystem {
2  constructor() {
3    this.draggedElement = null;
4    this.dragOffsetX = 0;
5    this.dragOffsetY = 0;
6    this.initialize();
7  }
8
9  initialize() {
10    const cards = document.querySelectorAll('.dinosaur-card');
11
12    cards.forEach(card => {
13      card.addEventListener('mousedown', (e) => this.handleDragStart(e));
14    });
15
16    document.addEventListener('mousemove', (e) => this.handleDragMove(e));
17    document.addEventListener('mouseup', (e) => this.handleDragEnd(e));
18
19    console.log('System drag & drop zainicjalizowany');
20  }
21
22  handleDragStart(event) {
23    this.draggedElement = event.currentTarget;
24
25    const rect = this.draggedElement.getBoundingClientRect();
26    this.dragOffsetX = event.clientX - rect.left;
27    this.dragOffsetY = event.clientY - rect.top;
28
29    this.draggedElement.style.position = 'fixed';
30    this.draggedElement.style.zIndex = '1000';
31    this.draggedElement.classList.add('dragging');
32
33    const dinoName = this.draggedElement.dataset.dinoName;
34    console.log(`Rozpoczęto przeciąganie karty: ${dinoName}`);
35
36    event.preventDefault(); // Prevent text selection
37  }
38
39  handleDragMove(event) {
40    if (!this.draggedElement) return;
41
42    const x = event.clientX - this.dragOffsetX;
43    const y = event.clientY - this.dragOffsetY;
44
45    this.draggedElement.style.left = x + 'px';
46    this.draggedElement.style.top = y + 'px';
47  }
48
49  handleDragEnd(event) {
50    if (!this.draggedElement) return;
51
52    const dinoName = this.draggedElement.dataset.dinoName;
53    console.log(`Zakończono przeciąganie karty: ${dinoName}`);
54
55    // Sprawdź czy karta została upuszczona w odpowiedniej strefie
56    const dropZone = this.findDropZone(event.clientX, event.clientY);
57
58    if (dropZone) {
59      console.log('Karta upuszczona w strefie:', dropZone.id);
60      this.handleDrop(this.draggedElement, dropZone);
61    } else {
62      console.log('Karta upuszczona poza strefą - powrót do pozycji początkowej');
63      this.returnToOriginalPosition();
64    }
65
66    this.draggedElement.classList.remove('dragging');
67    this.draggedElement = null;
68  }
69
70  findDropZone(x, y) {
71    const dropZones = document.querySelectorAll('.drop-zone');
72
73    for (const zone of dropZones) {
74      const rect = zone.getBoundingClientRect();
75
76      if (x >= rect.left && x <= rect.right &&
77          y >= rect.top && y <= rect.bottom) {
78        return zone;
79      }
80    }
81
82    return null;
83  }
84
85  handleDrop(card, dropZone) {
86    console.log('Dodawanie karty do strefy drop');
87    dropZone.appendChild(card);
88
89    // Reset stylów
90    card.style.position = '';
91    card.style.left = '';
92    card.style.top = '';
93    card.style.zIndex = '';
94  }
95
96  returnToOriginalPosition() {
97    // Animacja powrotu
98    this.draggedElement.style.transition = 'all 0.3s ease';
99    this.draggedElement.style.position = '';
100    this.draggedElement.style.left = '';
101    this.draggedElement.style.top = '';
102
103    setTimeout(() => {
104      this.draggedElement.style.transition = '';
105    }, 300);
106  }
107}
108
109// Inicjalizacja
110const dragSystem = new DinosaurCardDragSystem();

Wydarzenia klawiatury - kontrola z klawiatury

"Klawiatura," Ray wskazuje na panel kontrolny, "to drugi kluczowy sposób interakcji. Skróty klawiszowe, nawigacja, wprowadzanie danych."

Podstawowe wydarzenia klawiatury

1// keydown - klawisz naciśnięty (powtarza się gdy trzymany)
2document.addEventListener('keydown', (event) => {
3  console.log('=== KEYDOWN ===');
4  console.log('Klawisz:', event.key); // 'a', 'Enter', 'ArrowUp', etc.
5  console.log('Kod:', event.code); // 'KeyA', 'Enter', 'ArrowUp', etc.
6  console.log('KeyCode (deprecated):', event.keyCode);
7  console.log('Czy to powtórzenie:', event.repeat);
8  console.log('Ctrl:', event.ctrlKey);
9  console.log('Shift:', event.shiftKey);
10  console.log('Alt:', event.altKey);
11  console.log('Meta (Cmd/Win):', event.metaKey);
12});
13
14// keyup - klawisz puszczony
15document.addEventListener('keyup', (event) => {
16  console.log(`Puszczono klawisz: ${event.key}`);
17});
18
19// keypress - DEPRECATED! Nie używaj (użyj keydown zamiast tego)
20// keypress już nie jest wspierany we wszystkich przeglądarkach

Praktyczne skróty klawiszowe

1// System skrótów klawiszowych dla parku
2document.addEventListener('keydown', (event) => {
3  // Ctrl/Cmd + S - Zapisz
4  if ((event.ctrlKey || event.metaKey) && event.key === 's') {
5    event.preventDefault();
6    console.log('Zapisywanie konfiguracji parku...');
7    saveParkConfiguration();
8    return;
9  }
10
11  // Ctrl/Cmd + F - Szukaj
12  if ((event.ctrlKey || event.metaKey) && event.key === 'f') {
13    event.preventDefault();
14    console.log('Otwieranie wyszukiwarki dinozaurów');
15    openSearchDialog();
16    return;
17  }
18
19  // Escape - Zamknij wszystkie okna
20  if (event.key === 'Escape') {
21    console.log('Zamykanie wszystkich okien dialogowych');
22    closeAllModals();
23    return;
24  }
25
26  // Strzałki - Nawigacja po mapie
27  const arrowKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
28  if (arrowKeys.includes(event.key)) {
29    event.preventDefault();
30
31    const direction = {
32      'ArrowUp': 'north',
33      'ArrowDown': 'south',
34      'ArrowLeft': 'west',
35      'ArrowRight': 'east'
36    }[event.key];
37
38    console.log(`Przesunięcie mapy w kierunku: ${direction}`);
39    moveMap(direction);
40    return;
41  }
42
43  // Spacja - Pauza/Wznów monitoring
44  if (event.key === ' ') {
45    event.preventDefault();
46    console.log('Toggle pauzy monitoringu');
47    toggleMonitoringPause();
48    return;
49  }
50
51  // F1 - Pomoc
52  if (event.key === 'F1') {
53    event.preventDefault();
54    console.log('Otwieranie pomocy');
55    openHelp();
56    return;
57  }
58
59  // Cyfry 1-9 - Szybki dostęp do zagród
60  if (event.key >= '1' && event.key <= '9') {
61    const enclosureNumber = parseInt(event.key);
62    console.log(`Przełączanie na zagrodę #${enclosureNumber}`);
63    jumpToEnclosure(enclosureNumber);
64    return;
65  }
66});
67
68function saveParkConfiguration() {
69  console.log('Konfiguracja zapisana');
70}
71
72function openSearchDialog() {
73  console.log('Dialog wyszukiwania otwarty');
74}
75
76function closeAllModals() {
77  document.querySelectorAll('.modal').forEach(modal => {
78    modal.style.display = 'none';
79  });
80  console.log('Wszystkie okna zamknięte');
81}
82
83function moveMap(direction) {
84  console.log(`Mapa przesunięta w kierunku: ${direction}`);
85}
86
87function toggleMonitoringPause() {
88  console.log('Monitoring wstrzymany/wznowiony');
89}
90
91function openHelp() {
92  console.log('Pomoc otwarta');
93}
94
95function jumpToEnclosure(number) {
96  console.log(`Przejście do zagrody #${number}`);
97}

Walidacja wejścia z klawiatury

1const securityCodeInput = document.getElementById('security-code');
2const phoneInput = document.getElementById('phone-number');
3const nameInput = document.getElementById('visitor-name');
4
5// Tylko cyfry - kod bezpieczeństwa
6securityCodeInput.addEventListener('keydown', (event) => {
7  // Zezwól na klawisze funkcyjne
8  const allowedKeys = [
9    'Backspace', 'Delete', 'Tab', 'Escape', 'Enter',
10    'ArrowLeft', 'ArrowRight', 'Home', 'End'
11  ];
12
13  if (allowedKeys.includes(event.key)) {
14    return; // Pozwól
15  }
16
17  // Tylko cyfry
18  if (event.key >= '0' && event.key <= '9') {
19    // Maksymalnie 6 cyfr
20    if (securityCodeInput.value.length >= 6 && !event.ctrlKey && !event.metaKey) {
21      event.preventDefault();
22      console.log('Maksymalnie 6 cyfr');
23    }
24    return;
25  }
26
27  // Wszystko inne - blokuj
28  event.preventDefault();
29  console.log('Tylko cyfry są dozwolone');
30});
31
32// Formatowanie numeru telefonu
33phoneInput.addEventListener('keydown', (event) => {
34  const allowedKeys = [
35    'Backspace', 'Delete', 'Tab', 'Escape', 'Enter',
36    'ArrowLeft', 'ArrowRight', 'Home', 'End'
37  ];
38
39  if (allowedKeys.includes(event.key)) return;
40
41  // Tylko cyfry, spacje, myślniki, nawiasy
42  const phoneChars = /^[0-9 ()-]$/;
43
44  if (!phoneChars.test(event.key)) {
45    event.preventDefault();
46    console.log('Niedozwolony znak w numerze telefonu');
47  }
48});
49
50// Auto-kapitalizacja pierwszej litery
51nameInput.addEventListener('keyup', (event) => {
52  const value = nameInput.value;
53
54  if (value.length === 1) {
55    nameInput.value = value.toUpperCase();
56  }
57});

Kombinacja myszy i klawiatury

"Najlepsze interfejsy," mówi Ray, "łączą oba typy wejścia."

1const enclosureCards = document.querySelectorAll('.enclosure-card');
2let selectedCards = new Set();
3
4enclosureCards.forEach(card => {
5  card.addEventListener('click', (event) => {
6    const enclosureId = card.dataset.enclosureId;
7
8    // Różne zachowania w zależności od klawiszy modyfikujących
9    if (event.ctrlKey || event.metaKey) {
10      // Ctrl + Click = Toggle selekcji
11      console.log('Ctrl+Click: Toggle selekcji');
12
13      if (selectedCards.has(enclosureId)) {
14        selectedCards.delete(enclosureId);
15        card.classList.remove('selected');
16      } else {
17        selectedCards.add(enclosureId);
18        card.classList.add('selected');
19      }
20    } else if (event.shiftKey) {
21      // Shift + Click = Wybór zakresu
22      console.log('Shift+Click: Wybór zakresu');
23      selectRange(lastSelected, enclosureId);
24    } else {
25      // Zwykłe kliknięcie = Tylko ta jedna karta
26      console.log('Zwykłe kliknięcie: Pojedyncza selekcja');
27
28      selectedCards.clear();
29      document.querySelectorAll('.enclosure-card.selected').forEach(c => {
30        c.classList.remove('selected');
31      });
32
33      selectedCards.add(enclosureId);
34      card.classList.add('selected');
35    }
36
37    lastSelected = enclosureId;
38    updateSelectionInfo();
39  });
40});
41
42let lastSelected = null;
43
44function selectRange(startId, endId) {
45  console.log(`Wybieranie zakresu od ${startId} do ${endId}`);
46  // Logika wyboru zakresu
47}
48
49function updateSelectionInfo() {
50  console.log(`Wybrano ${selectedCards.size} zagród`);
51}
52
53// Nawigacja klawiaturą przez karty
54let focusedCardIndex = 0;
55const cards = Array.from(enclosureCards);
56
57document.addEventListener('keydown', (event) => {
58  // Tylko gdy żadne pole input nie jest aktywne
59  if (document.activeElement.tagName === 'INPUT') return;
60
61  switch(event.key) {
62    case 'ArrowRight':
63    case 'ArrowDown':
64      event.preventDefault();
65      focusedCardIndex = Math.min(focusedCardIndex + 1, cards.length - 1);
66      focusCard(focusedCardIndex);
67      break;
68
69    case 'ArrowLeft':
70    case 'ArrowUp':
71      event.preventDefault();
72      focusedCardIndex = Math.max(focusedCardIndex - 1, 0);
73      focusCard(focusedCardIndex);
74      break;
75
76    case 'Enter':
77    case ' ':
78      event.preventDefault();
79      cards[focusedCardIndex].click();
80      break;
81  }
82});
83
84function focusCard(index) {
85  cards.forEach(card => card.classList.remove('focused'));
86  cards[index].classList.add('focused');
87  cards[index].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
88
89  console.log(`Fokus na karcie #${index}: ${cards[index].dataset.enclosureName}`);
90}

Podsumowanie

"Wydarzenia myszy i klawiatury," podsumowuje Ray, "to fundament interaktywności. Podobnie jak różne sensory w Parku Jurajskim wykrywają różne typy zagrożeń, tak różne wydarzenia pozwalają na różnorodne interakcje użytkownika. Kluczem jest używanie odpowiednich wydarzeń do odpowiednich zadań."

Kluczowe wydarzenia myszy:

  • click
    ,
    dblclick
    ,
    contextmenu
    - kliknięcia
  • mousedown
    ,
    mouseup
    - kontrola przycisków
  • mousemove
    - śledzenie pozycji (używaj ostrożnie!)
  • mouseenter
    ,
    mouseleave
    - najechanie (nie bąbelkują)
  • mouseover
    ,
    mouseout
    - najechanie (bąbelkują)

Kluczowe wydarzenia klawiatury:

  • keydown
    - naciśnięcie klawisza (użyj tego)
  • keyup
    - puszczenie klawisza
  • event.key
    - znak/nazwa klawisza ('a', 'Enter', 'ArrowUp')
  • event.code
    - fizyczny klawisz ('KeyA', 'Enter', 'ArrowUp')
  • Klawisze modyfikujące:
    ctrlKey
    ,
    shiftKey
    ,
    altKey
    ,
    metaKey
Vai a CodeWorlds