We use cookies to enhance your experience on the site
CodeWorlds

Mouse and Keyboard Events

"In the security systems of Jurassic Park," Ray Arnold shows the large control panel, "we have different types of sensors. Motion detectors react to movement, pressure sensors to touch, sound sensors to noise. Each type requires a different response. In JavaScript, we have a similar situation - different input types require different handling."

Ray points to the monitor and keyboard. "The mouse and keyboard are the two primary sources of user interaction. Each has its own unique events and behaviors."

Mouse Events - The Full Range of Interactions

The mouse generates dozens of different event types, from simple clicks to advanced drag gestures.

Basic Click Events

1const dinosaurViewingArea = document.getElementById('viewing-area');
2
3// click - single click (most popular)
4dinosaurViewingArea.addEventListener('click', (event) => {
5  console.log('Single click in the viewing area');
6  console.log('Position:', event.clientX, event.clientY);
7
8  selectDinosaurAtPosition(event.clientX, event.clientY);
9});
10
11// dblclick - double click
12dinosaurViewingArea.addEventListener('dblclick', (event) => {
13  console.log('Double click - zooming in');
14
15  // Prevent text selection on double click
16  event.preventDefault();
17
18  zoomInOnDinosaur(event.clientX, event.clientY);
19});
20
21// auxclick - middle/other button click (not left)
22dinosaurViewingArea.addEventListener('auxclick', (event) => {
23  if (event.button === 1) {
24    console.log('Middle button - opening in new window');
25    openInNewWindow(event.target);
26  }
27});
28
29// contextmenu - right mouse button (context menu)
30dinosaurViewingArea.addEventListener('contextmenu', (event) => {
31  event.preventDefault(); // Block default menu
32
33  console.log('Right button - custom context menu');
34  showCustomContextMenu(event.clientX, event.clientY);
35});
36
37function selectDinosaurAtPosition(x, y) {
38  console.log(`Checking if a dinosaur is at position (${x}, ${y})`);
39  // Selection logic
40}
41
42function zoomInOnDinosaur(x, y) {
43  console.log(`Zooming in on position (${x}, ${y})`);
44  // Zoom logic
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('Context menu displayed');
54}

Mouse Button Events

1const controlPanel = document.getElementById('control-panel');
2
3// mousedown - mouse button pressed (before release)
4controlPanel.addEventListener('mousedown', (event) => {
5  console.log('Mouse button pressed');
6  console.log('Which button:', event.button); // 0=left, 1=middle, 2=right
7
8  if (event.button === 0) {
9    console.log('Left button - starting drag');
10    startDragging(event);
11  }
12});
13
14// mouseup - mouse button released
15controlPanel.addEventListener('mouseup', (event) => {
16  console.log('Mouse button released');
17
18  if (isDragging) {
19    console.log('Ending drag');
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(`Drag started from position (${dragStartX}, ${dragStartY})`);
34}
35
36function stopDragging(event) {
37  const deltaX = event.clientX - dragStartX;
38  const deltaY = event.clientY - dragStartY;
39
40  console.log(`Dragged by: X=${deltaX}px, Y=${deltaY}px`);
41
42  isDragging = false;
43}

Mouse Movement Events

1const parkMap = document.getElementById('interactive-park-map');
2
3// mousemove - mouse movement (CAUTION: very frequent!)
4// Use with care - can generate hundreds of events per second
5
6let mouseMoveCount = 0;
7
8parkMap.addEventListener('mousemove', (event) => {
9  mouseMoveCount++;
10
11  // Update cursor position
12  updateCursorPosition(event.clientX, event.clientY);
13
14  // If dragging, update position
15  if (isDragging) {
16    updateDragPosition(event.clientX, event.clientY);
17  }
18
19  // Log every 10th event (to avoid flooding the console)
20  if (mouseMoveCount % 10 === 0) {
21    console.log(`Mouse move #${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 - mouse enters the element's area (doesn't bubble!)
41parkMap.addEventListener('mouseenter', (event) => {
42  console.log('Mouse entered the park map');
43  parkMap.classList.add('mouse-active');
44  showMapControls();
45});
46
47// mouseleave - mouse leaves the element's area (doesn't bubble!)
48parkMap.addEventListener('mouseleave', (event) => {
49  console.log('Mouse left the park map');
50  parkMap.classList.remove('mouse-active');
51  hideMapControls();
52});
53
54// mouseover - mouse enters (bubbles - fires for children too!)
55parkMap.addEventListener('mouseover', (event) => {
56  if (event.target.classList.contains('enclosure-marker')) {
57    const enclosureName = event.target.dataset.name;
58    console.log(`Hovered over enclosure marker: ${enclosureName}`);
59    showEnclosureTooltip(event.target, enclosureName);
60  }
61});
62
63// mouseout - mouse leaves (bubbles)
64parkMap.addEventListener('mouseout', (event) => {
65  if (event.target.classList.contains('enclosure-marker')) {
66    console.log('Left enclosure marker');
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}

Practical Example - Drag System

"One of the most useful features," says Ray, "is 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('Drag & drop system initialized');
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(`Started dragging card: ${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(`Finished dragging card: ${dinoName}`);
54
55    // Check if the card was dropped in an appropriate zone
56    const dropZone = this.findDropZone(event.clientX, event.clientY);
57
58    if (dropZone) {
59      console.log('Card dropped in zone:', dropZone.id);
60      this.handleDrop(this.draggedElement, dropZone);
61    } else {
62      console.log('Card dropped outside zone - returning to original position');
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('Adding card to drop zone');
87    dropZone.appendChild(card);
88
89    // Reset styles
90    card.style.position = '';
91    card.style.left = '';
92    card.style.top = '';
93    card.style.zIndex = '';
94  }
95
96  returnToOriginalPosition() {
97    // Return animation
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// Initialization
110const dragSystem = new DinosaurCardDragSystem();

Keyboard Events - Keyboard Control

"The keyboard," Ray points to the control panel, "is the second key way of interaction. Keyboard shortcuts, navigation, data input."

Basic Keyboard Events

1// keydown - key pressed (repeats when held)
2document.addEventListener('keydown', (event) => {
3  console.log('=== KEYDOWN ===');
4  console.log('Key:', event.key); // 'a', 'Enter', 'ArrowUp', etc.
5  console.log('Code:', event.code); // 'KeyA', 'Enter', 'ArrowUp', etc.
6  console.log('KeyCode (deprecated):', event.keyCode);
7  console.log('Is it a repeat:', 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 - key released
15document.addEventListener('keyup', (event) => {
16  console.log(`Key released: ${event.key}`);
17});
18
19// keypress - DEPRECATED! Do not use (use keydown instead)
20// keypress is no longer supported in all browsers

Practical Keyboard Shortcuts

1// Keyboard shortcut system for the park
2document.addEventListener('keydown', (event) => {
3  // Ctrl/Cmd + S - Save
4  if ((event.ctrlKey || event.metaKey) && event.key === 's') {
5    event.preventDefault();
6    console.log('Saving park configuration...');
7    saveParkConfiguration();
8    return;
9  }
10
11  // Ctrl/Cmd + F - Search
12  if ((event.ctrlKey || event.metaKey) && event.key === 'f') {
13    event.preventDefault();
14    console.log('Opening dinosaur search');
15    openSearchDialog();
16    return;
17  }
18
19  // Escape - Close all windows
20  if (event.key === 'Escape') {
21    console.log('Closing all dialog windows');
22    closeAllModals();
23    return;
24  }
25
26  // Arrows - Map navigation
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(`Moving map in direction: ${direction}`);
39    moveMap(direction);
40    return;
41  }
42
43  // Space - Pause/Resume monitoring
44  if (event.key === ' ') {
45    event.preventDefault();
46    console.log('Toggle monitoring pause');
47    toggleMonitoringPause();
48    return;
49  }
50
51  // F1 - Help
52  if (event.key === 'F1') {
53    event.preventDefault();
54    console.log('Opening help');
55    openHelp();
56    return;
57  }
58
59  // Numbers 1-9 - Quick access to enclosures
60  if (event.key >= '1' && event.key <= '9') {
61    const enclosureNumber = parseInt(event.key);
62    console.log(`Switching to enclosure #${enclosureNumber}`);
63    jumpToEnclosure(enclosureNumber);
64    return;
65  }
66});
67
68function saveParkConfiguration() {
69  console.log('Configuration saved');
70}
71
72function openSearchDialog() {
73  console.log('Search dialog opened');
74}
75
76function closeAllModals() {
77  document.querySelectorAll('.modal').forEach(modal => {
78    modal.style.display = 'none';
79  });
80  console.log('All windows closed');
81}
82
83function moveMap(direction) {
84  console.log(`Map moved in direction: ${direction}`);
85}
86
87function toggleMonitoringPause() {
88  console.log('Monitoring paused/resumed');
89}
90
91function openHelp() {
92  console.log('Help opened');
93}
94
95function jumpToEnclosure(number) {
96  console.log(`Jumping to enclosure #${number}`);
97}

Keyboard Input Validation

1const securityCodeInput = document.getElementById('security-code');
2const phoneInput = document.getElementById('phone-number');
3const nameInput = document.getElementById('visitor-name');
4
5// Only digits - security code
6securityCodeInput.addEventListener('keydown', (event) => {
7  // Allow function keys
8  const allowedKeys = [
9    'Backspace', 'Delete', 'Tab', 'Escape', 'Enter',
10    'ArrowLeft', 'ArrowRight', 'Home', 'End'
11  ];
12
13  if (allowedKeys.includes(event.key)) {
14    return; // Allow
15  }
16
17  // Only digits
18  if (event.key >= '0' && event.key <= '9') {
19    // Maximum 6 digits
20    if (securityCodeInput.value.length >= 6 && !event.ctrlKey && !event.metaKey) {
21      event.preventDefault();
22      console.log('Maximum 6 digits');
23    }
24    return;
25  }
26
27  // Everything else - block
28  event.preventDefault();
29  console.log('Only digits are allowed');
30});
31
32// Phone number formatting
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  // Only digits, spaces, hyphens, parentheses
42  const phoneChars = /^[0-9 ()-]$/;
43
44  if (!phoneChars.test(event.key)) {
45    event.preventDefault();
46    console.log('Invalid character in phone number');
47  }
48});
49
50// Auto-capitalize first letter
51nameInput.addEventListener('keyup', (event) => {
52  const value = nameInput.value;
53
54  if (value.length === 1) {
55    nameInput.value = value.toUpperCase();
56  }
57});

Combining Mouse and Keyboard

"The best interfaces," says Ray, "combine both types of input."

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    // Different behavior depending on modifier keys
9    if (event.ctrlKey || event.metaKey) {
10      // Ctrl + Click = Toggle selection
11      console.log('Ctrl+Click: Toggle selection');
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 = Range selection
22      console.log('Shift+Click: Range selection');
23      selectRange(lastSelected, enclosureId);
24    } else {
25      // Normal click = Only this one card
26      console.log('Normal click: Single selection');
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(`Selecting range from ${startId} to ${endId}`);
46  // Range selection logic
47}
48
49function updateSelectionInfo() {
50  console.log(`Selected ${selectedCards.size} enclosures`);
51}
52
53// Keyboard navigation through cards
54let focusedCardIndex = 0;
55const cards = Array.from(enclosureCards);
56
57document.addEventListener('keydown', (event) => {
58  // Only when no input field is active
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(`Focus on card #${index}: ${cards[index].dataset.enclosureName}`);
90}

Summary

"Mouse and keyboard events," Ray summarizes, "are the foundation of interactivity. Just as different sensors in Jurassic Park detect different types of threats, different events allow for diverse user interactions. The key is using the right events for the right tasks."

Key mouse events:

  • click
    ,
    dblclick
    ,
    contextmenu
    - clicks
  • mousedown
    ,
    mouseup
    - button control
  • mousemove
    - position tracking (use carefully!)
  • mouseenter
    ,
    mouseleave
    - hovering (don't bubble)
  • mouseover
    ,
    mouseout
    - hovering (bubble)

Key keyboard events:

  • keydown
    - key pressed (use this one)
  • keyup
    - key released
  • event.key
    - character/key name ('a', 'Enter', 'ArrowUp')
  • event.code
    - physical key ('KeyA', 'Enter', 'ArrowUp')
  • Modifier keys:
    ctrlKey
    ,
    shiftKey
    ,
    altKey
    ,
    metaKey
Go to CodeWorlds