Skip to content

Allow scoped IDs equivalent to scoped classnames #6932

@thinkle

Description

@thinkle

Describe the problem

Like class names, IDs are global in vanilla HTML, which is fundamentally at odds with a component-based way of writing code. Compliant HTML code with labels, for example, is supposed to use matching ids on the for= attribute of the label and the id= attribute of the input.

Ideally, one could write a labelled input component like this in svelte:

<div>
<label for="text-input"><slot/></label>
<input id="text-input">
</div>

Unfortunately, if you use more than one instance of this component, it will break, so svelte-users have to roll-their-own ID generating solution to insure IDs do not conflict.

Currently, I believe the recommended solution is something like this:

<script context="module">
   let counter = 0
</script>
<script>
  counter += 1;
  let id = 'text-input-'+counter
</script>
<div>
  <label for={id}><slot/></label>
  <input id={id}>
</div>

That's a lot of boilerplate just to create a label and an input, and it's still fragile code since there remains the potential for id-collision with other components. What's more, it requires using a module-level script, which is a somewhat advanced feature of svelte, to accomplish a simple task required in just about any forms-based app.

Describe the proposed solution

Ideally, id= and for= attributes would work in a scoped way out of the box, just as classes do.

<div>
  <label for="text-input"><slot/></label>
  <input id="text-input">
</div>

Would generate code with hashed prefixes added to id= and for= attributes automatically.

<!-- output -->
<div>
  <label for="svelte-e9wl34-text-input"><slot/></label>
  <input id="svelte-e9wl34-text-input">
</div>

That would then require syntax to allow global values to be used when wanted, like so:

<div>
  <label for|global="text-input"><slot/></label>
  <input id|global="text-input">
</div>

Generates code without hashed prefixes added.

<!-- output -->
<div>
  <label for="text-input"><slot/></label>
  <input id="text-input">
</div>

Alternatives considered

Require special syntax to use scoped IDs

The above solution would be a breaking change. To avoid a breaking change, we could instead introduce a syntax for specifying local IDs. This would leave all existing code alone, but would mean that working with ids and classes would feel different.

<div>
  <label for|local="text-input"><slot/></label>
  <input id|local="text-input">
</div>

Provide some utility functions for generating IDs on the fly securely

<script>
   import {generateId} from 'svelte/future-magic';
</script>
<div>
  <label for={generateId("text-input")}><slot/></label>
  <input id={generateId("text-input")}>
</div>

This one is easy to do project-by-project if need be, so it would just be a convenience to add it to svelte. It doesn't feel nearly as elegant as building it into the syntax. That said, it would still be nice to standardize a way to do this and to implement it in a way that would prevent collisions between component libraries in the future.

Importance

nice to have

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions