Skip to content

Invalid Event constructor #219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
japgolly opened this issue Mar 31, 2016 · 4 comments · Fixed by #367
Closed

Invalid Event constructor #219

japgolly opened this issue Mar 31, 2016 · 4 comments · Fixed by #367

Comments

@japgolly
Copy link
Contributor

This:

new org.scalajs.dom.Event

breaks FireFox with

TypeError: Not enough arguments to Event.

Spec shows it should be: https://developer.mozilla.org/en-US/docs/Web/API/Event/Event

japgolly added a commit to japgolly/scalajs-react that referenced this issue Mar 31, 2016
@andersschuller
Copy link
Contributor

I don't know whether someone else has taken a look at this issue before, but I was curious to find out what it would take to fix it. It seemed simple enough at first glance...

Most of the events are currently defined as a hierarchy of simple classes or traits with default constructors:

class Event extends js.Object
class UIEvent extends Event
class FocusEvent extends UIEvent

There are a few exceptions to this. For example, the ClipboardEvent is defined like this:

class ClipboardEvent(`type`: String, settings: ClipboardEventInit) extends Event

However, MDN lists all four of these events as having constructors that take one string argument, and an optional dictionary argument which is used to initialise the fields of the event, including the fields of its superclasses. Therefore the signature of ClipboardEvent is also not quite correct, because the dictionary argument is not optional and because ClipboardEventInit only contains the fields used to initialise ClipboardEvent, but not those used to initialise Event.

I think in order to get the constructor signatures right, one would first have to define the hierarchy of types for the initialisation dictionaries. For the first three events listed above, that would look something like this:

trait EventInit extends js.Object {
  val bubbles: js.UndefOr[Boolean] = js.undefined
  val cancelable: js.UndefOr[Boolean] = js.undefined
  val scoped: js.UndefOr[Boolean] = js.undefined
  val composed: js.UndefOr[Boolean] = js.undefined
}

trait UIEventInit extends EventInit {
  val detail: js.UndefOr[Int] = js.undefined
  val view: js.UndefOr[Window] = js.undefined
}

trait FocusEventInit extends UIEventInit {
  val relatedTarget: js.UndefOr[EventTarget] = js.undefined
}

Then the events themselves can be defined like this:

class Event(typeArg: String, init: js.UndefOr[EventInit] = js.undefined) extends js.Object
class UIEvent(typeArg: String, init: js.UndefOr[UIEventInit] = js.undefined) extends Event(typeArg, init)
class FocusEvent(typeArg: String, init: js.UndefOr[FocusEventInit] = js.undefined) extends UIEvent(typeArg, init)

Given the number of events currently defined, I think it would not be straightforward to do this for all events. Unfortunately I don't see a good way to fix just the Event constructor without fixing all the events that extend it, but perhaps I've missed something important.

Related MDN pages:

@sjrd
Copy link
Member

sjrd commented Jan 7, 2017

Yep, I think your analysis is correct, indeed.

To mitigate the difficulty of migrating everything at once, we can start at the top of the hierarchy, and every time add a protected no-arg secondary constructor for the benefit of allowing subclasses to still compile, without having to fix them at once. For example:

class Event(typeArg: String, init: js.UndefOr[EventInit] = js.undefined) extends js.Object {
  protected def this() = this("") // for compatibility of subtypes
}

@ngbinh
Copy link

ngbinh commented May 9, 2017

now that we deprecate initEvent https://github.com/scala-js/scala-js-dom/blob/master/src/main/scala/org/scalajs/dom/raw/lib.scala#L5436, there is no way for me to create the kind of event I want. Something like:

        val evt = document.createEvent("HTMLEvents")
        evt.initEvent("scroll", canBubbleArg = true, cancelableArg = true)

What is the workaround for it?

@andersschuller
Copy link
Contributor

andersschuller commented May 11, 2017

While it's not particularly pretty or type-safe, I think one workaround would be to use js.Dynamic to call the Event constructor:

val eventInit = js.Dictionary("bubbles" -> true, "cancelable" -> false)
val event = js.Dynamic.newInstance(js.Dynamic.global.Event)("scroll", eventInit).asInstanceOf[dom.Event]

@sjrd sjrd closed this as completed in #367 May 16, 2019
sjrd pushed a commit that referenced this issue May 16, 2019
* Add valid constructors that accept type name and init, as well as
  type-safe constructor options `***EventInit`
* Removes invalid default (0 args) constructors since `new ***Event()`
  raises RuntimeError due to lack of required argument
* Add `@deprecated` based on MDN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants