Skip to content
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

[Svelte 5] Events should be allowed to be camelCased #10044

Closed
D-Marc1 opened this issue Dec 31, 2023 · 16 comments
Closed

[Svelte 5] Events should be allowed to be camelCased #10044

D-Marc1 opened this issue Dec 31, 2023 · 16 comments

Comments

@D-Marc1
Copy link

D-Marc1 commented Dec 31, 2023

Describe the problem

While it's a welcomed addition that Svelte 5 is becoming more like vanilla JS, when it comes to events, like onclick={}, onsubmit={}, etc., it's weird why it doesn't allow for it to be camelCased, like so: onClick={} and onSubmit={}.

Even vanilla HTML/JS allows you to camelCase the event name, so it doesn't make sense why Svelte 5 is restricting it to only allowed nocase. Additionally, React is the opposite, where it only allows camelCase, as functions and variables are typically camelCased in JS. If camelCased events were allowed in Svelte 5, it would be easier to do the shorthand syntax of {onClick} (of course it normally should be more descriptive anyway).

Describe the proposed solution

I propose to allow both camelCase and nocase syntax for native events, much like how it already work.

So following would both work:

<button onClick={}>My button</button>
<button onclick={}>My button</button>

Alternatives considered

There's no alternative that I'm aware of.

Importance

would make my life easier

@Thiagolino8
Copy link

The problem is that this would allow having 2 different listeners for the same event

<button onClick={increment} onclick={decrement}>My button</button>

Which seems to be something the team wants to avoid

Considering that it is also no longer possible to call global functions using strings

<dialog id="dialog">My dialog</dialog>
<button onclick="dialog.openModal()">Open modal</button>

It seems that being a superset of HTML is no longer an objective

@D-Marc1
Copy link
Author

D-Marc1 commented Jan 1, 2024

If it has to be one or the other, then I think camelCased onClick={} would be preferred by most, as camelCasing is the standard in JavaScript. I personally like how React sticks to camelCasing.

@brunnerh
Copy link
Member

brunnerh commented Jan 1, 2024

There was some internal discussion on this before, nobody wanted the camelCasing option (even knowing of its advantages).

While HTML attributes have no case sensitivity, the DOM properties do and they are all lowercase.
It might be possible to technically allow both, but that would encourage/lead to inconsistencies.

Components have to declare their properties (unless spread), so there either would be no option to use a different casing or there needs to be some normalization which then might mean that the property is spelled differently in declaration and usage site.


@Thiagolino8 No longer supporting something like onclick="dialog.openModal()" is a rare exception since such handlers generally should not be used in plain HTML anyway, as they rely on global scope. In most cases that should be easy enough to work around, too.

@Malix-Labs
Copy link

  • Comment from @brunnerh :

    There was some internal discussion on this before

    Have you found back its source?

@Malix-Labs
Copy link

Moreover,
When reading, I found myself often confusing on:click and onclick, which probably would not or less happen with onClick

@Thiagolino8
Copy link

If it has to be one or the other, then I think camelCased onClick={} would be preferred by most, as camelCasing is the standard in JavaScript. I personally like how React sticks to camelCasing.

If it has to be one or the other, then I think lowercase is certainly the best choice.
Because Svelte already uses lowercase, the migration would be to just removing the : after the on

@elliott-with-the-longest-name-on-github
Copy link
Contributor

I was reading through the specs for this stuff to see what it said, and I think sticking with lowercase is the way to go. The whatwg spec for event listeners states:

For both of these two ways, the event handler is exposed through a name, which is a string that always starts with "on" and is followed by the name of the event for which the handler is intended.

So it seems that the spec intends for event handlers to be of the form on{eventname}. The question, then, is what case does the spec say the event names are in? Well, further down that document, there are lists of a ton of handlers along with their corresponding events, and all of them are lowercase.

As far as I can tell, the spec says nothing about whether HTML parsers should lowercase on{eventName} handlers, but HTML parsers are famously permissive of non-spec stuff, so it wouldn't surprise me if the fact that onClick works is just an artifact of some guy who thought it would be nice at some point way in the past. My vote in this case is for sticking to the spec -- which lists all events as lowercase.

@elliott-with-the-longest-name-on-github
Copy link
Contributor

This was originally posted in response to a comment that was deleted, then reposted with different content.

This is a bad argument, as camelCasing is supported in HTML.

Where is this in the spec? (Not trying to call you out, I was just looking for it and couldn't find it 😅)

@D-Marc1
Copy link
Author

D-Marc1 commented Jan 3, 2024

If it has to be one or the other, then I think lowercase is certainly the best choice.
Because Svelte already uses lowercase, the migration would be to just removing the : after the on

The colon is much more comparable to camelCase than it is nocase. The colon is effectively a type of casing, as it indicates two separate words:

  • on:click()
  • onClick()

Both examples of camelCasing and colon:casing indicate two separate words: on and click.

Using onclick is much less readable and is one single word, which is illogical.

There's no doubt that camelCasing is the best choice for developer experience, as this is what JavaScript developers are used to and it's how React does it too.

Where is this in the spec? (Not trying to call you out, I was just looking for it and couldn't find it 😅)

Try out using: <button onclick="onclick()">Click me</button> and <button onClick="onClick()">Click me</button>. Both work. Svelte would uniquely be restricting itself to nocase, when HTML would accept both.

@elliott-with-the-longest-name-on-github
Copy link
Contributor

Try out using: Click me and Click me. Both work. Svelte would uniquely be restricting itself to nocase, when HTML would accept both.

Again, where is it in the spec? "HTML would accept both" is a nonsense statement; HTML is a spec, not an entity that makes decisions. HTML parsers may incorrectly allow camelcased event handler names, but they would be deviating from the spec in doing so.

I tried your example. Turns out all of these work:

image

Unless you're a fan of cLICKing buttons, I wouldn't use the argument "whatever the HTML parser accepts is valid HTML". HTML parsers do their absolute best not to break even in the weirdest circumstances. They'll automatically insert missing closing tags, for example. This defensive permissiveness should not be viewed as approval. Additionally, at least as far as Chrome is concerned, event handler names should be lowercased -- that screenshot (and the associated test) was actually really difficult to get, because any time Chrome performed any save action on the DOM, it would reformat it to:

image

Which is, well, the right way to do it.

@D-Marc1
Copy link
Author

D-Marc1 commented Jan 3, 2024

"HTML would accept both" is a nonsense statement; HTML is a spec, not an entity that makes decisions. HTML parsers may incorrectly allow camelcased event handler names, but they would be deviating from the spec in doing so.

Nope, it's not nonsense. What's nonsense is claiming that Svelte is simply following HTML specs, and that's it, when that's simply false. Svelte is artificially restricting itself to nocase, when even HTML doesn't.

I tried your example. Turns out all of these work:

That's great that you tried it. Now you see that Svelte is in fact diverging away from how HTML works.

Unless you're a fan of cLICKing buttons, I wouldn't use the argument "whatever the HTML parser accepts is valid HTML". HTML parsers do their absolute best not to break even in the weirdest circumstances.

I'm a fan of being as strict as vanilla HTML, that's correct. It's an excellent argument to want to stick to how HTML does things as much as possible to reduce the learning curve. However, if it has to be one, then there's no doubt that camelCase would be the better choice for DX and would closer resemble the old on:click way, which does effectively read as two words. Using nocasing will always be the least readable.

@brunnerh
Copy link
Member

brunnerh commented Jan 3, 2024

As I stated earlier, HTML is case insensitive, everything gets automatically lowercased.

See the "get an attribute" algorithm:

If it is in the range 0x41 (A) to 0x5A (Z)
Append the code point b+0x20 to attribute name (where b is the value of the byte at position). (This converts the input to lowercase.)

@D-Marc1
Copy link
Author

D-Marc1 commented Jan 3, 2024

So why not keep it like that in Svelte too, so that both nocase and camelCase works?

Not to mention take for example Svelte's decision to move away from the double mustache {{}} to simply use the single curly brace {}. One of the mentioned reasons was because of users being more accustomed to the single brace in React.

#1318

So why not apply this logic to events to allow camelCasing?

@brunnerh
Copy link
Member

brunnerh commented Jan 3, 2024

Again, see my previous comment, it causes various inconsistencies.

@elliott-with-the-longest-name-on-github
Copy link
Contributor

See the "get an attribute" algorithm:

Thank you! That's what I've been searching for this whole time. This means that it's officially acceptable to use case-insensitive attributes, though the canonical name (at least, for events and event handlers) is lowercase. That's a bit more permissive, and gives us more room to reason. My personal opinion is that in cases like this, one way to do things is better than two (working in a codebase where both onclick and onClick are acceptable would be... gross). My personal opinion aside, though, there are other reasons we really need to pick one or the other (full case-insensitivity is a complete no-go for reasons I'll outline below).

First, why do we have to chose one or the other, and why is case-insensitivity not an option?:

  1. Types - it's (relatively) easy to type DOM elements with their correct attributes when you have one way to case things -- just make some gigantic interfaces with all of the spec interfaces. Two ways would still be doable (just duplicate each attribute in its camelCase and nocase versions), but a less-than-ideal developer experience (if you typed on, you'd get double autocomplete for every event handler). Case-insensitivity would be basically impossible -- you'd have to use recursive types to generate every possible combination of casings, give up typing altogether, or use some generics-based Lowercase / extends checks, which would be the most feasible option by far, but would be a horrible developer experience for a number of reasons (weird autocomplete and weird error messages being foremost among them).
  2. Component props would either have to be transformed to be case-insensitive or developers would have to declare really weird props to keep with Svelte's conventions:
    // either this
    let { onclick, onClick } = $props({});
    let _onClick = $derived(onclick ?? onClick); // which one should take precedence?
    
    // or Svelte tries to detect event handlers and normalize them? which would be really bonkers and bug-prone
  3. Fragmentation -- there is absolutely no reason to have component libraries have to maintain two different definitions for every event handler they export. Convention over... well, not configuration, but pointless options. This is the kind of thing people would have to have ESLint rules specifically to maintain consistency in their codebases, which is usually a sign of a bad API choice, IMO.

So if we have to choose I can see arguments for both. onClick is more readable and is used by React / JSX. onclick is the canonical name for the event handler (and, while this might not matter in HTML, it does matter on DOM properties, where case-sensitivity is important). IMO onclick being the canonical name for the handler is enough to win the argument on its own, especially because the DOM properties are also lowercase, but I also don't really buy the readability argument as much as I would in most circumstances -- this isn't like reading normal text. There are a finite (and relatively small) number of event handlers. Once you've seen onbeforeunload once, you've seen it and you'll never think twice about it again. Additionally, if we camelCased event handlers, we'd also have to camelCase events, and at that point it would be weird to not camelCase attributes as well, which just drifts further and further from the spec and their corresponding DOM properties.

@dummdidumm
Copy link
Member

Elliot argued nicely for why we're doing it the way we're currently doing it. There's one more crucial runtime-related argument: Writing <button onClick=..>..</button> currently means that you listen to the Click (not the upper C), not the click event. This is to support custom events that might have weird casing. Lowercasing them all would lead to us having to maintain a list of all events at runtime, bloating the bundle size and playing catchup whenever a new event is added.

I'm therefore going to close this issue.

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

No branches or pull requests

6 participants