Webcomponents: [idea] Not having <slot> element get in the way of structure.

Created on 18 Oct 2016  路  17Comments  路  Source: WICG/webcomponents

It seems like having <slot> (or <content>) elements gets in the way of the structure that markup aims to convey.

Would it perhaps be better to preserve the structure of the tree by using attributes instead of elements, for defining slots?

For example,

instead of

#shadow-root
  <div>
    <slot name="some-slot">
    </slot>
  </div>

what about something like

#shadow-root
  <div define-slot="some-slot">
  </div>

?

Or, perhaps even a JavaScript-only approach, so that we don't taint attributes:

#shadow-root
  <div class="some-slot">
  </div>
root.defineSlot(root.querySelector('div'), 'some-slot')

Since _the only way to define shadow roots_ is via JavaScript, it could possibly make sense that defining slots can be a JavaScript-only procedure.

Not having to use HTML to define slots would prevent extra elements (that don't behave like elements) from getting in the way of the structural design conveyed in the markup.

Just a thought.

shadow-dom

Most helpful comment

Having slots as HTML element prevents styling empty containers, e.g.

<my-layout-component>
  <slot name="foo"></slot>
</my-layout-component>

Say I need to hide my-layout-component if empty, this won't work:

my-layout-component:empty {
  display: none;
}

because there's the slot, even if I do not place any element in it via slot="foo".

Being able to do something like this would make it work, though:

<my-layout-component is="slot" name="foo">
</my-layout-component>

All 17 comments

It seems like having <slot> (or <content>) elements gets in the way of the structure that markup aims to convey.

Could you clarify what you mean by this?

@rniwa It just seems like defining elements should be a tool for defining content that will be rendered, and the <slot> is not content and is not rendered, it is a distribution tool. Maybe the distribution tool doesn't need to be an actual element, but maybe some way to register by name which elements will be distribution points (as in the above example).

For example, here's another idea, using selectors:

<!-- Light tree -->
<div class='host'>
  <img slot="some-slot" ... />
</div>
let root = document.querySelector('.host').attachShadow({mode: 'closed'})
root.innerHTML = `
  <!-- Shadow tree -->
  <div>
    <div class="image-holder">
    </div>
  </div>
`
root.defineSlot('some-slot', '.image-holder') // uses the first match, similar to querySelector

// alternatively, could take an actual instance, where the instance must exist in the shadow tree:
root.defineSlot('some-slot', root.querySelector('.image-holder'))

Get what I mean? It would eliminate from having to use an element that doesn't really behave like an element, <slot> is like a weird special element that really isn't one.

I don't think that works. You need to a way to specify where in DOM tree the assigned nodes would go. e.g. if you had <div slot="slotName"><img><br></div> then where in div are assigned nodes supposed to go? If this behaves identically to <slot name="slotName"><img><br></slot>, then your proposal doesn't accomplish anything beyond replacing a slot element with a slot attribute or a equivalent JS API that returns a regular element into a slot. And turning an arbitrary element into a slot element is an extremely complicated operation that at least we, WebKit, are not wiling to implement.

@hayatoito @annevk @travisleithead : any opinions about this?

What I like about React, is that distribution into the inner tree doesn't involve elements, which is what indirectly inspired me to write this idea. In React, the HTML markup is separate from distribution logic. The markup is just that, markup representing content, not any sort of logic. For example, in React:

function SomeReactComponent(props) {
  return (
    <div slot="slotName">
      <img /><br />
      {props.children /* we insert children here */ }
    </div>
  )
}

or

function SomeReactComponent(props) {
  return (
    <div slot="slotName">
      <img />
      {props.children /* we insert children here */ }
      <br />
    </div>
  )
}

or

function SomeReactComponent(props) {
  return (
    <div slot="slotName">
      {props.children.filter(child => someCondition ) /* we insert some children here */ }
      <img />
      <br />
      {props.children.filter(child => !someCondition ) /* and put the rest here */ }
    </div>
  )
}

See how that is separate from the markup which defines the content?

HTML doesn't have stuff like {} though, we really only have elements and attributes. Personally I like <slot> as it is, having something you can querySelector makes it easier to debug.

Yeah, we're adding slot element because we can't really support something like {}. If you're working on a framework like React, you can just emit slot element from {}.

I think it's a bit too late to brainstorm alternatives that lack a clear processing model and wide support.

@trusktr I recommend reaching out to some folks first when you have an idea to see if it's worth pursuing. https://wiki.whatwg.org/wiki/FAQ#Is_there_a_process_for_adding_new_features_to_a_specification.3F is a good guide. And when features are close to shipping, it's a bit too late for radical changes. I encourage focusing on enhancing the status quo.

<slot> is a declarative way to express node distribution.

@trusktr
You might want to see #60 if you are interested in imperative API for node distribution.
Imperative API is one of the hardest problems.

@annevk

a good guide.

Thanks for the link, well noted.

@rniwa

If you're working on a framework like React, you can just emit slot element from {}.

If I were to write components using React instead of Web Components, I would just completely ignore ShadowDOM because composition is all handled by (encapsulated in) React components.

I'm finding that React's composition model is currently a lot more flexible and easier to work with than ShadowDOM, but the advantage of writing components with Web Components is that they will work in any view layer, not just React.

What I meant to say in my previous comment about React is that distribution in React is completely separate from the HTML markup (and same with other view layers like Handlebars), so the distribution mechanism isn't mixed with content.

What problems are you finding because there are slot elements in the DOM?

Not really any problems per se, but more about semantic meaning. In React (for example), the separation between content (HTML) and logic (distribution with JavaScript) is cleaner I think, that's all. Maybe things like ParentNode.prototype.distributedChildren, ParentNode.prototype.isDistributionPoint (boolean that is true when the node can havedistributed children), ChildNode.prototype.distributedParent (which is always null in closed roots), and a childdistributed event on the ParentNode would be more along the lines of the semantic separation, along with JS-only API to define distribution points that does not involve any elements (no <slot> or <content> elements). And the reason why JS-only API would be acceptable is because creating shadow roots is already JS-only to begin with.

Just thoughts, but knowing the web, the discussion here is probably moot since the API is already released.

Having slots as HTML element prevents styling empty containers, e.g.

<my-layout-component>
  <slot name="foo"></slot>
</my-layout-component>

Say I need to hide my-layout-component if empty, this won't work:

my-layout-component:empty {
  display: none;
}

because there's the slot, even if I do not place any element in it via slot="foo".

Being able to do something like this would make it work, though:

<my-layout-component is="slot" name="foo">
</my-layout-component>

@heruan please file a new issue on the problem, please don't assume a particular solution (e.g., it might be more appropriate to have some kind of selector to address that need).

701

Was this page helpful?
0 / 5 - 0 ratings