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

Named slots and the slot attribute requirement #726

Closed
cary-smith opened this issue Jan 26, 2018 · 21 comments
Closed

Named slots and the slot attribute requirement #726

cary-smith opened this issue Jan 26, 2018 · 21 comments

Comments

@cary-smith
Copy link

cary-smith commented Jan 26, 2018

So after building quite a few components and reading through #343, I was wondering if you could clarify something for me:

Given a complex shadowDOM template with multiple named-slot locations I am curious why an element selector cannot be used if it is only focused on the children of the element involved. In-house we are trying to develop multiple elements that have specific styling inherent to them. Some of these elements have specified child elements that can be used which are not dissimilar to a <tr> in a <table> element (the <tr> does not really have a use outside a <table>).

For the developers that use our elements, there is now required boilerplate to place that child element in the named slot due to the required slot="someSlotName". This can be overcome by the child element generating its own slot="someSlotName" attribute but it cannot do so during its own construction, only during the connectedCallback. This creates quite a bit of unnecessary churn during the initial connection of an element because it starts unslotted then gets moved/projected into place (the most noticeable slowdown happens when using polyfills like Polymer).

Staying within the context of only selecting from the element children and without pretending to know the complexity involved, I am curious if the v0 selector="someElementType" or the more preferred method @rniwa had mentioned about interface selection are possible . Maybe something like

<slot interface="SomeElement" />

This would allow certain element types to be slotted according to the design of the shadow template instead of the boilderplate attribute.....kinda like <table> knows where to place a <thead> element without <thead slot="head">.

If I am foolishly missing some pattern, please let me know.

Thoughts?

@PaulHMason
Copy link

You basically want the selectors that the Shadow DOM V0 element supported. I'm not sure why that feature was dropped.

@cary-smith
Copy link
Author

@PaulHMason I would be OK with that but for extension purposes I prefer the interface concept @rniwa
had mentioned more for the reasons provided. The slot concept works fantastically for more generic web components but in the case of a more specific slotting concept it becomes excess when there is an element type designed for the slot. I have attempted:

  • Having the child element define its slot attribute: Though this works....its not very efficient
  • Having the parent scan its children in the constructor and set the slots on particular element types: Although a little better, this does not handle dynamically added elements well, even via MutationObserver.
  • Having the developers add the boilerplate slot="slotLocation" to the custom children: It can easily be forgotten due to redundancy. When we have an parent <my-element> with a child <my-element-info>, they may miss the required <my-element-info slot="info">.

Being able to define the slot via interface would be very powerful indeed and reduce redundancy in more complex elements.

@rniwa
Copy link
Collaborator

rniwa commented Feb 20, 2018

Like we previously stated, Apple's preference here is to add imperative slotting API as well as providing a mechanism to slot elements on their interfaces so that the inheritance would work. We need to be careful as to in which realm and when JS states are read (since the timing of [[Get]] is observable).

@rniwa
Copy link
Collaborator

rniwa commented Feb 20, 2018

Also see the issue #719.

@cary-smith
Copy link
Author

Thanks for the update! So if I am reading you correctly, a more declarative approach may be in the works depending on some selector work?

@annevk
Copy link
Collaborator

annevk commented Feb 20, 2018

@cary-smith no, @rniwa states that only an imperative solution would be acceptable to Apple. Thus far we haven't been able to figure out such an API.

@cary-smith
Copy link
Author

cary-smith commented Feb 20, 2018

@annevk Thanks for the clarification! I may have misunderstood the

as well as providing a mechanism to slot elements on their interfaces

I thought that might mean we may be able to slot children according to child element's interface instead of the slot="someSlotName" approach (in a future implementation). Was I incorrect there?

@rniwa
Copy link
Collaborator

rniwa commented Feb 21, 2018

@cary-smith no, @rniwa states that only an imperative solution would be acceptable to Apple. Thus far we haven't been able to figure out such an API.

That's not true. We're against using the CSS selector as a mechanism to match an element but not against providing a declarative mechanism to select an element based on its type such as the interface object. Our only requirement is that the slotting mechanism be performant; i.e. not as slow as evaluating a CSS selector whenever a node gets modified.

@annevk
Copy link
Collaborator

annevk commented Feb 21, 2018

My bad. So <slot element="h1"> would be an acceptable extension?

@rniwa
Copy link
Collaborator

rniwa commented Feb 21, 2018

Sure. In fact, we suggested that but Google veto'ed it saying we should keep it simple.

@TakayoshiKochi
Copy link
Member

An episode: at moving Blink's all UA shadow implementations from V0 to V1, <details> element could not be represented only with web-exposed V1 APIs, to find summary:first-of-type in its children, so we had to introduce an internal UA-shadow only mechanism to pick that element.

Maybe using CSS Selector is too powerful to be performant, but we need to find options to explore for extending the current capability. So we need use cases that can't be represented with V1 APIs.

@domenic
Copy link
Collaborator

domenic commented Feb 21, 2018

So here are two concrete use cases I'd like to solve:

  • details/summary, i.e. a way to select summary:first-of-type
  • select/option or similar list-like constructs, e.g. this list markup. Here you want to put every child (or perhaps every child with a given tag name?) into a container in the shadow DOM, without requiring that users add slot="slot1", slot="slot2", slot="slot3", ... to all the children.

I am curious about the internal mechanisms Blink and WebKit use for their UA shadow roots, and whether we could clean those up into a proposal for something to expose to the web platform.

@hayatoito
Copy link
Contributor

hayatoito commented Feb 22, 2018

Blink's internal mechanism can't be a starter of a proposal.
We are using hard-coding for each element which uses UA shadow roots in C++.

@domenic
Copy link
Collaborator

domenic commented Feb 22, 2018

Sure, but I was curious what the hard-coded handling looked like in C++, and whether it would make sense to expose hooks that would allow similar JS code to be inserted in the appropriate places?

@rniwa
Copy link
Collaborator

rniwa commented Feb 22, 2018

In WebKit, we added the ability to override what the slot name for a given element is, and the slot assignment for details overrides that logic on the first summary element:
https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/html/HTMLDetailsElement.cpp?rev=224390#L70

Exposing this API would involve exposing the timing at which slotting would happen so it's kind of non-starter.

@hayatoito
Copy link
Contributor

hayatoito commented Feb 22, 2018

Sure, but I was curious what the hard-coded handling looked like in C++, and whether it would make sense to expose hooks that would allow similar JS code to be inserted in the appropriate places?

Here is hard coding.
https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/dom/SlotAssignment.cpp?l=22

This can be considered as a kind of imperative APIs, if we would expose. Web developers have to provide a function hook for a shadow root (or for the definition of a custom element), as such:

function canBeAssigned(host_child, internal_slot) {
  // ...
  return true | false;
}

@hayatoito
Copy link
Contributor

hayatoito commented Feb 22, 2018

Exposing this API would involve exposing the timing at which slotting would happen so it's kind of non-starter.

Yup, I don't want to expose the timing of slot assigning. That should be kept as an engine's internal matter.

@rniwa
Copy link
Collaborator

rniwa commented Feb 22, 2018

I think a better approach for JS is to provide an imperative API to set the list of nodes assigned to a slot. e.g. slot.replaceAssignment(nodes); where nodes would replace whatever the slot shows. Such a succinct API is still quite powerful. It's true that we can't truly implement details and summary because the DOM mutation won't be reflected immediately but that's probably the best we can do today without blowing up this feature into a worklet insanity.

@Jamesernator
Copy link

I think slot.assign(element) would be easier than .replaceAssignment(nodes) as you could easily just cherry pick elements you need to change slots without needing to filter slot.assignedNodes(). This would effectively be identical to setting slot on the element but would be unobservable from outside of the shadow root (for closed mode anyway, I suppose .assignedSlot would work as normal for open mode).

For example suppose you had some component that was a slideshow or something, you would probably want to write something like this:

class SlideShow extends HTMLElement {
    ...

    _changeToNextSlide() {
        const hiddenSlides = this._shadowRoot.querySelector("#hiddenSlides")
        const visibleSlide = this._shadowRoot.querySelector("#visibleSlide")
        hiddenSlides.assign(this.children[this.currentSlide])
        this.currentSlide += 1
        visibleSlide.assign(this.children[this.currentSlide])
    }
}

@rniwa
Copy link
Collaborator

rniwa commented Feb 22, 2018

The problem with slot.assign is that you'd need some mechanism to un-assign nodes as well. This also prevents use cases that require re-ordering of elements.

@annevk
Copy link
Collaborator

annevk commented Mar 5, 2018

Rough consensus that we'd prefer an imperative API in Tokyo.

@annevk annevk closed this as completed Mar 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants