tachys/html/
event.rs

1use crate::{
2    html::attribute::{
3        maybe_next_attr_erasure_macros::next_attr_combine, Attribute,
4        NamedAttributeKey,
5    },
6    renderer::{CastFrom, RemoveEventHandler, Rndr},
7    view::{Position, ToTemplate},
8};
9use send_wrapper::SendWrapper;
10use std::{
11    borrow::Cow,
12    cell::RefCell,
13    fmt::Debug,
14    marker::PhantomData,
15    ops::{Deref, DerefMut},
16    rc::Rc,
17};
18use wasm_bindgen::convert::FromWasmAbi;
19
20/// A cloneable event callback.
21pub type SharedEventCallback<E> = Rc<RefCell<dyn FnMut(E)>>;
22
23/// A function that can be called in response to an event.
24pub trait EventCallback<E>: 'static {
25    /// Runs the event handler.
26    fn invoke(&mut self, event: E);
27
28    /// Converts this into a cloneable/shared event handler.
29    fn into_shared(self) -> SharedEventCallback<E>;
30}
31
32impl<E: 'static> EventCallback<E> for SharedEventCallback<E> {
33    fn invoke(&mut self, event: E) {
34        let mut fun = self.borrow_mut();
35        fun(event)
36    }
37
38    fn into_shared(self) -> SharedEventCallback<E> {
39        self
40    }
41}
42
43impl<F, E> EventCallback<E> for F
44where
45    F: FnMut(E) + 'static,
46{
47    fn invoke(&mut self, event: E) {
48        self(event)
49    }
50
51    fn into_shared(self) -> SharedEventCallback<E> {
52        Rc::new(RefCell::new(self))
53    }
54}
55
56/// An event listener with a typed event target.
57pub struct Targeted<E, T> {
58    event: E,
59    el_ty: PhantomData<T>,
60}
61
62impl<E, T> Targeted<E, T> {
63    /// Returns the inner event.
64    pub fn into_inner(self) -> E {
65        self.event
66    }
67
68    /// Returns the event's target, as an HTML element of the correct type.
69    pub fn target(&self) -> T
70    where
71        T: CastFrom<crate::renderer::types::Element>,
72
73        crate::renderer::types::Event: From<E>,
74        E: Clone,
75    {
76        let ev = crate::renderer::types::Event::from(self.event.clone());
77        Rndr::event_target(&ev)
78    }
79}
80
81impl<E, T> Deref for Targeted<E, T> {
82    type Target = E;
83
84    fn deref(&self) -> &Self::Target {
85        &self.event
86    }
87}
88
89impl<E, T> DerefMut for Targeted<E, T> {
90    fn deref_mut(&mut self) -> &mut Self::Target {
91        &mut self.event
92    }
93}
94
95impl<E, T> From<E> for Targeted<E, T> {
96    fn from(event: E) -> Self {
97        Targeted {
98            event,
99            el_ty: PhantomData,
100        }
101    }
102}
103
104/// Creates an [`Attribute`] that will add an event listener to an element.
105pub fn on<E, F>(event: E, cb: F) -> On<E, F>
106where
107    F: FnMut(E::EventType) + 'static,
108    E: EventDescriptor + Send + 'static,
109    E::EventType: 'static,
110    E::EventType: From<crate::renderer::types::Event>,
111{
112    On {
113        event,
114        #[cfg(feature = "reactive_graph")]
115        owner: reactive_graph::owner::Owner::current().unwrap_or_default(),
116        cb: (!cfg!(feature = "ssr")).then(|| SendWrapper::new(cb)),
117    }
118}
119
120/// Creates an [`Attribute`] that will add an event listener with a typed target to an element.
121#[allow(clippy::type_complexity)]
122pub fn on_target<E, T, F>(
123    event: E,
124    mut cb: F,
125) -> On<E, Box<dyn FnMut(E::EventType)>>
126where
127    T: HasElementType,
128    F: FnMut(Targeted<E::EventType, <T as HasElementType>::ElementType>)
129        + 'static,
130    E: EventDescriptor + Send + 'static,
131    E::EventType: 'static,
132
133    E::EventType: From<crate::renderer::types::Event>,
134{
135    on(event, Box::new(move |ev: E::EventType| cb(ev.into())))
136}
137
138/// An [`Attribute`] that adds an event listener to an element.
139pub struct On<E, F> {
140    event: E,
141    #[cfg(feature = "reactive_graph")]
142    owner: reactive_graph::owner::Owner,
143    cb: Option<SendWrapper<F>>,
144}
145
146impl<E, F> Clone for On<E, F>
147where
148    E: Clone,
149    F: Clone,
150{
151    fn clone(&self) -> Self {
152        Self {
153            event: self.event.clone(),
154            #[cfg(feature = "reactive_graph")]
155            owner: self.owner.clone(),
156            cb: self.cb.clone(),
157        }
158    }
159}
160
161impl<E, F> On<E, F>
162where
163    F: EventCallback<E::EventType>,
164    E: EventDescriptor + Send + 'static,
165    E::EventType: 'static,
166    E::EventType: From<crate::renderer::types::Event>,
167{
168    /// Attaches the event listener to the element.
169    pub fn attach(
170        self,
171        el: &crate::renderer::types::Element,
172    ) -> RemoveEventHandler<crate::renderer::types::Element> {
173        fn attach_inner(
174            el: &crate::renderer::types::Element,
175            cb: Box<dyn FnMut(crate::renderer::types::Event)>,
176            name: Cow<'static, str>,
177            // TODO investigate: does passing this as an option
178            // (rather than, say, having a const DELEGATED: bool)
179            // add to binary size?
180            delegation_key: Option<Cow<'static, str>>,
181        ) -> RemoveEventHandler<crate::renderer::types::Element> {
182            match delegation_key {
183                None => Rndr::add_event_listener(el, &name, cb),
184                Some(key) => {
185                    Rndr::add_event_listener_delegated(el, name, key, cb)
186                }
187            }
188        }
189
190        let mut cb = self.cb.expect("callback removed before attaching").take();
191
192        #[cfg(feature = "tracing")]
193        let span = tracing::Span::current();
194
195        let cb = Box::new(move |ev: crate::renderer::types::Event| {
196            #[cfg(all(debug_assertions, feature = "reactive_graph"))]
197            let _rx_guard =
198                reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
199            #[cfg(feature = "tracing")]
200            let _tracing_guard = span.enter();
201
202            let ev = E::EventType::from(ev);
203
204            #[cfg(feature = "reactive_graph")]
205            self.owner.with(|| cb.invoke(ev));
206            #[cfg(not(feature = "reactive_graph"))]
207            cb.invoke(ev);
208        }) as Box<dyn FnMut(crate::renderer::types::Event)>;
209
210        attach_inner(
211            el,
212            cb,
213            self.event.name(),
214            (E::BUBBLES && cfg!(feature = "delegation"))
215                .then(|| self.event.event_delegation_key()),
216        )
217    }
218
219    /// Attaches the event listener to the element as a listener that is triggered during the capture phase,
220    /// meaning it will fire before any event listeners further down in the DOM.
221    pub fn attach_capture(
222        self,
223        el: &crate::renderer::types::Element,
224    ) -> RemoveEventHandler<crate::renderer::types::Element> {
225        fn attach_inner(
226            el: &crate::renderer::types::Element,
227            cb: Box<dyn FnMut(crate::renderer::types::Event)>,
228            name: Cow<'static, str>,
229        ) -> RemoveEventHandler<crate::renderer::types::Element> {
230            Rndr::add_event_listener_use_capture(el, &name, cb)
231        }
232
233        let mut cb = self.cb.expect("callback removed before attaching").take();
234
235        #[cfg(feature = "tracing")]
236        let span = tracing::Span::current();
237
238        let cb = Box::new(move |ev: crate::renderer::types::Event| {
239            #[cfg(all(debug_assertions, feature = "reactive_graph"))]
240            let _rx_guard =
241                reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
242            #[cfg(feature = "tracing")]
243            let _tracing_guard = span.enter();
244
245            let ev = E::EventType::from(ev);
246
247            #[cfg(feature = "reactive_graph")]
248            self.owner.with(|| cb.invoke(ev));
249            #[cfg(not(feature = "reactive_graph"))]
250            cb.invoke(ev);
251        }) as Box<dyn FnMut(crate::renderer::types::Event)>;
252
253        attach_inner(el, cb, self.event.name())
254    }
255}
256
257impl<E, F> Debug for On<E, F>
258where
259    E: Debug,
260{
261    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262        f.debug_tuple("On").field(&self.event).finish()
263    }
264}
265
266impl<E, F> Attribute for On<E, F>
267where
268    F: EventCallback<E::EventType>,
269    E: EventDescriptor + Send + 'static,
270    E::EventType: 'static,
271
272    E::EventType: From<crate::renderer::types::Event>,
273{
274    const MIN_LENGTH: usize = 0;
275    type AsyncOutput = Self;
276    // a function that can be called once to remove the event listener
277    type State = (
278        crate::renderer::types::Element,
279        Option<RemoveEventHandler<crate::renderer::types::Element>>,
280    );
281    type Cloneable = On<E, SharedEventCallback<E::EventType>>;
282    type CloneableOwned = On<E, SharedEventCallback<E::EventType>>;
283
284    #[inline(always)]
285    fn html_len(&self) -> usize {
286        0
287    }
288
289    #[inline(always)]
290    fn to_html(
291        self,
292        _buf: &mut String,
293        _class: &mut String,
294        _style: &mut String,
295        _inner_html: &mut String,
296    ) {
297    }
298
299    #[inline(always)]
300    fn hydrate<const FROM_SERVER: bool>(
301        self,
302        el: &crate::renderer::types::Element,
303    ) -> Self::State {
304        let cleanup = if E::CAPTURE {
305            self.attach_capture(el)
306        } else {
307            self.attach(el)
308        };
309        (el.clone(), Some(cleanup))
310    }
311
312    #[inline(always)]
313    fn build(self, el: &crate::renderer::types::Element) -> Self::State {
314        let cleanup = if E::CAPTURE {
315            self.attach_capture(el)
316        } else {
317            self.attach(el)
318        };
319        (el.clone(), Some(cleanup))
320    }
321
322    #[inline(always)]
323    fn rebuild(self, state: &mut Self::State) {
324        let (el, prev_cleanup) = state;
325        if let Some(prev) = prev_cleanup.take() {
326            if let Some(remove) = prev.into_inner() {
327                remove();
328            }
329        }
330        *prev_cleanup = Some(if E::CAPTURE {
331            self.attach_capture(el)
332        } else {
333            self.attach(el)
334        });
335    }
336
337    fn into_cloneable(self) -> Self::Cloneable {
338        On {
339            cb: self.cb.map(|cb| SendWrapper::new(cb.take().into_shared())),
340            #[cfg(feature = "reactive_graph")]
341            owner: self.owner,
342            event: self.event,
343        }
344    }
345
346    fn into_cloneable_owned(self) -> Self::CloneableOwned {
347        On {
348            cb: self.cb.map(|cb| SendWrapper::new(cb.take().into_shared())),
349            #[cfg(feature = "reactive_graph")]
350            owner: self.owner,
351            event: self.event,
352        }
353    }
354
355    fn dry_resolve(&mut self) {}
356
357    async fn resolve(self) -> Self::AsyncOutput {
358        self
359    }
360
361    fn keys(&self) -> Vec<NamedAttributeKey> {
362        vec![]
363    }
364}
365
366impl<E, F> NextAttribute for On<E, F>
367where
368    F: EventCallback<E::EventType>,
369    E: EventDescriptor + Send + 'static,
370    E::EventType: 'static,
371
372    E::EventType: From<crate::renderer::types::Event>,
373{
374    next_attr_output_type!(Self, NewAttr);
375
376    fn add_any_attr<NewAttr: Attribute>(
377        self,
378        new_attr: NewAttr,
379    ) -> Self::Output<NewAttr> {
380        next_attr_combine!(self, new_attr)
381    }
382}
383
384impl<E, F> ToTemplate for On<E, F> {
385    #[inline(always)]
386    fn to_template(
387        _buf: &mut String,
388        _class: &mut String,
389        _style: &mut String,
390        _inner_html: &mut String,
391        _position: &mut Position,
392    ) {
393    }
394}
395
396/// A trait for converting types into [web_sys events](web_sys).
397pub trait EventDescriptor: Clone {
398    /// The [`web_sys`] event type, such as [`web_sys::MouseEvent`].
399    type EventType: FromWasmAbi;
400
401    /// Indicates if this event bubbles. For example, `click` bubbles,
402    /// but `focus` does not.
403    ///
404    /// If this is true, then the event will be delegated globally if the `delegation`
405    /// feature is enabled. Otherwise, event listeners will be directly attached to the element.
406    const BUBBLES: bool;
407
408    /// Indicates if this event should be handled during the capture phase.
409    const CAPTURE: bool = false;
410
411    /// The name of the event, such as `click` or `mouseover`.
412    fn name(&self) -> Cow<'static, str>;
413
414    /// The key used for event delegation.
415    fn event_delegation_key(&self) -> Cow<'static, str>;
416
417    /// Return the options for this type. This is only used when you create a [`Custom`] event
418    /// handler.
419    #[inline(always)]
420    fn options(&self) -> Option<&web_sys::AddEventListenerOptions> {
421        None
422    }
423}
424
425/// A wrapper that tells the framework to handle an event during the capture phase.
426#[derive(Debug, Clone, PartialEq, Eq)]
427pub struct Capture<E> {
428    inner: E,
429}
430
431/// Wraps an event to indicate that it should be handled during the capture phase.
432pub fn capture<E>(event: E) -> Capture<E> {
433    Capture { inner: event }
434}
435
436impl<E: EventDescriptor> EventDescriptor for Capture<E> {
437    type EventType = E::EventType;
438
439    const CAPTURE: bool = true;
440    const BUBBLES: bool = E::BUBBLES;
441
442    fn name(&self) -> Cow<'static, str> {
443        self.inner.name()
444    }
445
446    fn event_delegation_key(&self) -> Cow<'static, str> {
447        self.inner.event_delegation_key()
448    }
449}
450
451/// A custom event.
452#[derive(Debug)]
453pub struct Custom<E: FromWasmAbi = web_sys::Event> {
454    name: Cow<'static, str>,
455    options: Option<SendWrapper<web_sys::AddEventListenerOptions>>,
456    _event_type: PhantomData<fn() -> E>,
457}
458
459impl<E: FromWasmAbi> Clone for Custom<E> {
460    fn clone(&self) -> Self {
461        Self {
462            name: self.name.clone(),
463            options: self.options.clone(),
464            _event_type: PhantomData,
465        }
466    }
467}
468
469impl<E: FromWasmAbi> EventDescriptor for Custom<E> {
470    type EventType = E;
471
472    fn name(&self) -> Cow<'static, str> {
473        self.name.clone()
474    }
475
476    fn event_delegation_key(&self) -> Cow<'static, str> {
477        format!("$$${}", self.name).into()
478    }
479
480    const BUBBLES: bool = false;
481
482    #[inline(always)]
483    fn options(&self) -> Option<&web_sys::AddEventListenerOptions> {
484        self.options.as_deref()
485    }
486}
487
488impl<E: FromWasmAbi> Custom<E> {
489    /// Creates a custom event type that can be used within
490    /// [`OnAttribute::on`](crate::prelude::OnAttribute::on), for events
491    /// which are not covered in the [`ev`](crate::html::event) module.
492    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
493        Self {
494            name: name.into(),
495            options: None,
496            _event_type: PhantomData,
497        }
498    }
499
500    /// Modify the [`AddEventListenerOptions`] used for this event listener.
501    ///
502    /// ```rust
503    /// # use tachys::prelude::*;
504    /// # use tachys::html;
505    /// # use tachys::html::event as ev;
506    /// # fn custom_event() -> impl Render {
507    /// let mut non_passive_wheel = ev::Custom::new("wheel");
508    /// non_passive_wheel.options_mut().set_passive(false);
509    ///
510    /// let canvas =
511    ///     html::element::canvas().on(non_passive_wheel, |e: ev::WheelEvent| {
512    ///         // handle event
513    ///     });
514    /// # canvas
515    /// # }
516    /// ```
517    ///
518    /// [`AddEventListenerOptions`]: web_sys::AddEventListenerOptions
519    pub fn options_mut(&mut self) -> &mut web_sys::AddEventListenerOptions {
520        // It is valid to construct a `SendWrapper` here because
521        // its inner data will only be accessed in the browser's main thread.
522        self.options.get_or_insert_with(|| {
523            SendWrapper::new(web_sys::AddEventListenerOptions::new())
524        })
525    }
526}
527
528macro_rules! generate_event_types {
529  {$(
530    $( #[$does_not_bubble:ident] )?
531    $( $event:ident )+ : $web_event:ident
532  ),* $(,)?} => {
533    ::paste::paste! {
534      $(
535        #[doc = "The `" [< $($event)+ >] "` event, which receives [" $web_event "](web_sys::" $web_event ") as its argument."]
536        #[derive(Copy, Clone, Debug)]
537        #[allow(non_camel_case_types)]
538        pub struct [<$( $event )+ >];
539
540        impl EventDescriptor for [< $($event)+ >] {
541          type EventType = web_sys::$web_event;
542
543          #[inline(always)]
544          fn name(&self) -> Cow<'static, str> {
545            stringify!([< $($event)+ >]).into()
546          }
547
548          #[inline(always)]
549          fn event_delegation_key(&self) -> Cow<'static, str> {
550            concat!("$$$", stringify!([< $($event)+ >])).into()
551          }
552
553          const BUBBLES: bool = true $(&& generate_event_types!($does_not_bubble))?;
554        }
555      )*
556    }
557  };
558
559  (does_not_bubble) => { false }
560}
561
562generate_event_types! {
563  // =========================================================
564  // WindowEventHandlersEventMap
565  // =========================================================
566  #[does_not_bubble]
567  after print: Event,
568  #[does_not_bubble]
569  before print: Event,
570  #[does_not_bubble]
571  before unload: BeforeUnloadEvent,
572  #[does_not_bubble]
573  gamepad connected: GamepadEvent,
574  #[does_not_bubble]
575  gamepad disconnected: GamepadEvent,
576  hash change: HashChangeEvent,
577  #[does_not_bubble]
578  language change: Event,
579  #[does_not_bubble]
580  message: MessageEvent,
581  #[does_not_bubble]
582  message error: MessageEvent,
583  #[does_not_bubble]
584  offline: Event,
585  #[does_not_bubble]
586  online: Event,
587  #[does_not_bubble]
588  page hide: PageTransitionEvent,
589  #[does_not_bubble]
590  page show: PageTransitionEvent,
591  pop state: PopStateEvent,
592  rejection handled: PromiseRejectionEvent,
593  #[does_not_bubble]
594  storage: StorageEvent,
595  #[does_not_bubble]
596  unhandled rejection: PromiseRejectionEvent,
597  #[does_not_bubble]
598  unload: Event,
599
600  // =========================================================
601  // GlobalEventHandlersEventMap
602  // =========================================================
603  #[does_not_bubble]
604  abort: UiEvent,
605  animation cancel: AnimationEvent,
606  animation end: AnimationEvent,
607  animation iteration: AnimationEvent,
608  animation start: AnimationEvent,
609  aux click: MouseEvent,
610  before input: InputEvent,
611  before toggle: Event, // web_sys does not include `ToggleEvent`
612  #[does_not_bubble]
613  blur: FocusEvent,
614  #[does_not_bubble]
615  can play: Event,
616  #[does_not_bubble]
617  can play through: Event,
618  change: Event,
619  click: MouseEvent,
620  #[does_not_bubble]
621  close: Event,
622  composition end: CompositionEvent,
623  composition start: CompositionEvent,
624  composition update: CompositionEvent,
625  context menu: MouseEvent,
626  #[does_not_bubble]
627  cue change: Event,
628  dbl click: MouseEvent,
629  drag: DragEvent,
630  drag end: DragEvent,
631  drag enter: DragEvent,
632  drag leave: DragEvent,
633  drag over: DragEvent,
634  drag start: DragEvent,
635  drop: DragEvent,
636  #[does_not_bubble]
637  duration change: Event,
638  #[does_not_bubble]
639  emptied: Event,
640  #[does_not_bubble]
641  ended: Event,
642  #[does_not_bubble]
643  error: ErrorEvent,
644  #[does_not_bubble]
645  focus: FocusEvent,
646  #[does_not_bubble]
647  focus in: FocusEvent,
648  #[does_not_bubble]
649  focus out: FocusEvent,
650  form data: Event, // web_sys does not include `FormDataEvent`
651  #[does_not_bubble]
652  got pointer capture: PointerEvent,
653  input: Event,
654  #[does_not_bubble]
655  invalid: Event,
656  key down: KeyboardEvent,
657  key press: KeyboardEvent,
658  key up: KeyboardEvent,
659  #[does_not_bubble]
660  load: Event,
661  #[does_not_bubble]
662  loaded data: Event,
663  #[does_not_bubble]
664  loaded metadata: Event,
665  #[does_not_bubble]
666  load start: Event,
667  lost pointer capture: PointerEvent,
668  mouse down: MouseEvent,
669  #[does_not_bubble]
670  mouse enter: MouseEvent,
671  #[does_not_bubble]
672  mouse leave: MouseEvent,
673  mouse move: MouseEvent,
674  mouse out: MouseEvent,
675  mouse over: MouseEvent,
676  mouse up: MouseEvent,
677  #[does_not_bubble]
678  pause: Event,
679  #[does_not_bubble]
680  play: Event,
681  #[does_not_bubble]
682  playing: Event,
683  pointer cancel: PointerEvent,
684  pointer down: PointerEvent,
685  #[does_not_bubble]
686  pointer enter: PointerEvent,
687  #[does_not_bubble]
688  pointer leave: PointerEvent,
689  pointer move: PointerEvent,
690  pointer out: PointerEvent,
691  pointer over: PointerEvent,
692  pointer up: PointerEvent,
693  #[does_not_bubble]
694  progress: ProgressEvent,
695  #[does_not_bubble]
696  rate change: Event,
697  reset: Event,
698  #[does_not_bubble]
699  resize: UiEvent,
700  #[does_not_bubble]
701  scroll: Event,
702  #[does_not_bubble]
703  scroll end: Event,
704  security policy violation: SecurityPolicyViolationEvent,
705  #[does_not_bubble]
706  seeked: Event,
707  #[does_not_bubble]
708  seeking: Event,
709  select: Event,
710  #[does_not_bubble]
711  selection change: Event,
712  select start: Event,
713  slot change: Event,
714  #[does_not_bubble]
715  stalled: Event,
716  submit: SubmitEvent,
717  #[does_not_bubble]
718  suspend: Event,
719  #[does_not_bubble]
720  time update: Event,
721  #[does_not_bubble]
722  toggle: Event,
723  touch cancel: TouchEvent,
724  touch end: TouchEvent,
725  touch move: TouchEvent,
726  touch start: TouchEvent,
727  transition cancel: TransitionEvent,
728  transition end: TransitionEvent,
729  transition run: TransitionEvent,
730  transition start: TransitionEvent,
731  #[does_not_bubble]
732  volume change: Event,
733  #[does_not_bubble]
734  waiting: Event,
735  webkit animation end: Event,
736  webkit animation iteration: Event,
737  webkit animation start: Event,
738  webkit transition end: Event,
739  wheel: WheelEvent,
740
741  // =========================================================
742  // WindowEventMap
743  // =========================================================
744  D O M Content Loaded: Event, // Hack for correct casing
745  #[does_not_bubble]
746  device motion: DeviceMotionEvent,
747  #[does_not_bubble]
748  device orientation: DeviceOrientationEvent,
749  #[does_not_bubble]
750  orientation change: Event,
751
752  // =========================================================
753  // DocumentAndElementEventHandlersEventMap
754  // =========================================================
755  copy: ClipboardEvent,
756  cut: ClipboardEvent,
757  paste: ClipboardEvent,
758
759  // =========================================================
760  // DocumentEventMap
761  // =========================================================
762  fullscreen change: Event,
763  fullscreen error: Event,
764  pointer lock change: Event,
765  pointer lock error: Event,
766  #[does_not_bubble]
767  ready state change: Event,
768  visibility change: Event,
769}
770
771// Export `web_sys` event types
772use super::{
773    attribute::{
774        maybe_next_attr_erasure_macros::next_attr_output_type, NextAttribute,
775    },
776    element::HasElementType,
777};
778#[doc(no_inline)]
779pub use web_sys::{
780    AnimationEvent, BeforeUnloadEvent, ClipboardEvent, CompositionEvent,
781    CustomEvent, DeviceMotionEvent, DeviceOrientationEvent, DragEvent,
782    ErrorEvent, Event, FocusEvent, GamepadEvent, HashChangeEvent, InputEvent,
783    KeyboardEvent, MessageEvent, MouseEvent, PageTransitionEvent, PointerEvent,
784    PopStateEvent, ProgressEvent, PromiseRejectionEvent,
785    SecurityPolicyViolationEvent, StorageEvent, SubmitEvent, TouchEvent,
786    TransitionEvent, UiEvent, WheelEvent,
787};