Learning Book

CustomEvent и собственные события

Кастомные события

CustomEvent

// Создание кастомного события
const event = new CustomEvent('user-login', {
  bubbles: true,        // всплывает ли событие
  cancelable: true,     // можно ли отменить через preventDefault()
  detail: {            // данные события
    userId: 42,
    username: 'Иван',
    timestamp: Date.now()
  }
})

// Отправка события
document.dispatchEvent(event)

// Или на конкретном элементе
const btn = document.querySelector('#login-btn')
btn.dispatchEvent(new CustomEvent('clicked', { bubbles: true }))

Подписка на кастомные события

// Слушаем кастомное событие
document.addEventListener('user-login', (e) => {
  const { userId, username } = e.detail
  console.log(`Вошёл пользователь: ${username} (ID: ${userId})`)
  updateUI(username)
})

// Кастомные события всплывают если bubbles: true
document.querySelector('.modal').addEventListener('user-login', (e) => {
  // поймает событие, отправленное из дочернего элемента
})

Паттерн: компонентное взаимодействие

// Компонент Cart отправляет событие при добавлении товара
class Cart {
  constructor(element) {
    this.element = element
    this.items = []
  }

  addItem(product) {
    this.items.push(product)

    // Уведомляем другие части приложения
    this.element.dispatchEvent(new CustomEvent('cart:item-added', {
      bubbles: true,
      detail: {
        product,
        total: this.items.length
      }
    }))
  }
}

// Компонент Badge слушает и обновляет счётчик
class CartBadge {
  constructor(element, cart) {
    this.element = element

    // Подписываемся на событие из Cart
    cart.element.addEventListener('cart:item-added', (e) => {
      this.update(e.detail.total)
    })
  }

  update(count) {
    this.element.textContent = count
  }
}
Соглашение по именованию кастомных событий: namespace:action (например, cart:item-added, modal:close, form:submit). Это предотвращает конфликты с нативными событиями.

Event Hub — центральная шина событий

// Простой event bus через EventTarget
class EventBus extends EventTarget {
  emit(type, detail) {
    this.dispatchEvent(new CustomEvent(type, { detail }))
  }
}

const bus = new EventBus()

// Подписка в одном компоненте
bus.addEventListener('theme-changed', (e) => {
  applyTheme(e.detail.theme)
})

// Отправка из другого
bus.emit('theme-changed', { theme: 'dark' })