- From: Joe Pea <notifications@github.com>
- Date: Sun, 12 Jul 2020 13:18:47 -0700
- To: w3c/webcomponents <webcomponents@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <w3c/webcomponents/issues/883/657270047@github.com>
@Danny-Engelman Thanks for the thoughts! > `::slotted(.foo) + P` will never work because: **There is no `<P>` -IN- shadowDOM** That selector would do what it says verbatim: select the sibling `<p>` element of the element targeted by `::slotted(.foo)`. The sibling `<p>` element (if any) would be in the same light tree as the element targeted by `::slotted(.foo)`. I wouldn't expect it to select anything in the shadow DOM as it wouldn't make intuitive sense. (I could imagine a `::shadow-sibling()` selector for that.) If the sibling `<p>` element is not distributed and therefore not visible, there's no styling applied to it (intuitively). However if the author of a custom element adds a `<slot>` element later (f.e. due to some user interaction or other feature) and the sibling `<p>` element thus becomes distributed and therefore visible, then the styling would apply. It would also be convenient for getting references: `root.querySelector('::slotted(.foo) + p')` The code for `querySelector('::slotted(*) span')` roughly equates to this: ```js const d = document.querySelector('div') const r = d.attachShadow({mode: 'open'}) r.innerHTML = ` <div> <slot></slot> </div> ` const start = performance.now() // Emulate querySelector('::slotted(*) span') const slots = r.querySelectorAll('slot') const queryResult = [] // outer loops gets result for `::slotted(*)` for (let i = 0, l = slots.length, slot, elems; i < l; i += 1) { slot = slots[i] elems = slot.assignedElements() // inner loop gets result for ` span` for (let j = 0, l = elems.length; j < l; j += 1) { queryResult.push(...elems[j].querySelectorAll('span')) } } const end = performance.now() const total = end - start console.log(`The query took ${total} milliseconds.`) console.log(...queryResult) ``` and similar for polyfilling other queries. Putting it this way, it is easy to imagine how `querySelector('::slotted(*) + p')` would work, by replacing ```js queryResult.push(...elems[j].querySelectorAll('.bar')) ``` with ```js if (elems[j].nextElementSibling?.tagName === 'P') queryResult.push(elems[j].nextElementSibling) ``` > A reason why `::slotted` became a simple selector is performance The performance claims are speculative. :thinking: As with any selector, users should save the results to variables rather than executing them over and over (f.e. in an animation). > _**I presume**_ that makes a more advanced `::slotted` selector a performance issue because content can now come from anywhere in the AST, not just a sub-tree I'm not sure what you mean by "anywhere in the AST, not just a sub-tree", but what I propose is that the postfix combinators run in the light tree (which to me intuitively makes sense). What I've proposed actually doesn't change the performance of `::slotted(whatever)`. In the query `::slotted(whatever) > ul li.bar` the performance of `::slotted(whatever)` is exactly the same as it is today. Then the performance of ` > ul li.bar` (which is an existing feature today) would be the same as if you tacked that onto the result of any other non-slotted selector. --- Here's a live example of the above code emulating `querySelector('::slotted(*) span')`: https://codepen.io/trusktr/pen/0a71670d0da693a4758bc4f36463452f?editors=1010 On my computer the output is `The query took 0.005993600000097104 milliseconds on average.`. It ranged from 0.005 to 0.15 when I tried it many times. Probably I had other things going on during the slower ones. I really don't think there's a performance issue here that is any different than all other existing selectors. I don't think we can really use this claim against allowing combinators postfixed to `::slotted(whatever)`. I believe that performance claim is a moot point because we can easily make selectors that will perform a lot worse than `::slotted(whatever) > ul li.bar` using the selector features of today. --- > ``` > <style> > /* This correctly styles the reflected (slotted) content: */ > p span { > border: 1px solid deeppink; > } > </style> > ``` There's a problem with this: this is how a _user styling_ can be applied to content that is passed to a custom element. This does not allow a _custom element author_ to perform the styling. Allowing custom element authors to perform the styling of the light tree (without users having to think about it) is a valid use case and makes a variety of things possible (f.e. user provides markup and the output looks good without the user also having to stick additional styles or stylesheets outside of the custom element). --- > Key here is slotted content is **not a copy**, and **not the original**, but a **reflection** I think teaching people using the standard spec terminology is the best way to go, so that the concepts match up across articles and guides. > (with the limitation only one reflection is possible) But the terminology that you use really has no bearing on the topic; it only describes what currently exists. If the spec were expanded to allow the combinators postfixed after `::slotted` selectors, then you would simply update your custom language to reflect the new reality (no pun intended! :smile:). --- On a similar note, `querySelector('::slotted(.foo)')` doesn't even work although it is a valid selector. Demo: https://codepen.io/trusktr/pen/d4a45f1efc9eccc0fdb20164566bada4?editors=1010 (shows `null` in console) --- In my opinion there's really not a good reason why a custom element author should not be able to style deeper elements within slotted elements (and also select them with `querySelector`). -- You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub: https://github.com/w3c/webcomponents/issues/883#issuecomment-657270047
Received on Sunday, 12 July 2020 20:19:01 UTC