> On Feb 16, 2014, at 1:21 AM, Alex Russell <slightly...@google.com> wrote: > >> On Sun, Feb 16, 2014 at 12:52 AM, Ryosuke Niwa <rn...@apple.com> wrote: >>> On Feb 16, 2014, at 12:42 AM, Ryosuke Niwa <rn...@apple.com> wrote: >>> >>>> On Feb 15, 2014, at 11:30 PM, Alex Russell <slightly...@google.com> wrote: >>>> >>>>> On Sat, Feb 15, 2014 at 4:57 PM, Ryosuke Niwa <rn...@apple.com> wrote: >>>>> Hi all, >>>>> >>>>> I’d like to propose one solution for >>>>> >>>>> [Shadow]: Specify imperative API for node distribution >>>>> https://www.w3.org/Bugs/Public/show_bug.cgi?id=18429 >>>>> >>>>> because select content attribute doesn’t satisfy the needs of >>>>> framework/library authors to support conditionals in their templates, >>>>> and doesn’t satisfy my random image element use case below. >>>>> >>>>> >>>>> == Use Case == >>>>> Random image element is a custom element that shows one of child img >>>>> elements chosen uniformally random. >>>>> >>>>> e.g. the markup of a document that uses random-image-element may look >>>>> like this: >>>>> <random-image-element> >>>>> <img src="kitten.jpg"> >>>>> <img src="cat.jpg"> >>>>> <img src="webkitten.jpg"> >>>>> </random-image-element> >>>>> >>>>> random-image-element displays one out of the three img child elements >>>>> when a user clicks on it. >>>>> >>>>> As an author of this element, I could modify the DOM and add style >>>>> content attribute directly on those elements >>>>> but I would rather use shadow DOM to encapsulate the implementation. >>>>> >>>>> >>>>> == API Proposal == >>>>> >>>>> Add two methods void add(Element) and void remove(Element) to content >>>>> element. >>>>> (We can give them more descriptive names. I matched select element for >>>>> now). >>>>> >>>>> Each content element has an ordered list of *explicitly inserted nodes*. >>>>> >>>>> add(Element element) must act according to the following algorithm: >>>>> If the content element's shadow host's node tree doesn't contain >>>>> _element_, throw HierarchyRequestError. >>>>> If element is already in some other content element's _explicitly >>>>> inserted nodes_ >>>>> then call remove with _element_ on that content element. >>>>> Append _element_ to the end of _explicitly inserted nodes_. >>>>> >>>>> remove(Element element) must act according to the following algorithm: >>>>> If the content element's _explicitly inserted nodes_ does not contain >>>>> _element_, throw NotFoundError. >>>> >>>> Throwing exceptions is hostile to usability. >>> >>> If people are so inclined, we don’t have to throw an exception and silently >>> fail. >>>>> Remove _element_ from _explicitly inserted nodes_. >>>>> >>>>> The idea here is that _explicitly inserted nodes_ of an insertion point A >>>>> would be the list of distributed nodes of A but >>>>> I haven't figured out exactly how _explicitly inserted nodes_ should >>>>> interact with select content attribute. >>>>> >>>>> I think the simplest model would be _explicitly inserted nodes_ simply >>>>> overriding whatever select content attribute was >>>>> trying to do but I don't have a strong opinion about how they should >>>>> interact yet. >>>>> >>>>> I don't think it makes sense to support redistributions, etc... at least >>>>> in the initial API. >>>>> >>>>> >>>>> This proposal has an advantage over the existing proposal on >>>>> https://www.w3.org/Bugs/Public/show_bug.cgi?id=18429: >>>>> It doesn't require UA calling back to JS constantly to match elements >>>>> Point 1 implies we don't expose when distribution happens for select >>>>> content attribute. >>>> This doesn't seem like progress. I'd hope an imperative API would, >>>> instead, be used to explain how the existing system works and then propose >>>> layering that both accommodates the existing system and opens new areas >>>> for programmatic use. >>>> >>>> We can imagine such a system for programmatic Shadow DOM with some sort of >>>> distribute(Element) callback that can be over-ridden and use add/remove >>>> methods to do final distribution. >>> >>> The problem here is that such a callback must be called on every node upon >>> any state change because UAs have no way of knowing what causes >>> redistribution for a given component. As as a matter of fact, some use >>> cases may involve changing the node distributions based on some JS objects >>> state. And having authors codify such conditions for UAs is much more >>> cumbersome than letting them re-distribute nodes at their will. >> >> To give you more concrete example, in the case of my random image element, >> how can UA notice that user clicking on the element should trigger >> reconstruction of the composed tree? > > Isn't the stated design of the custom element that it re-constructs the > composed tree with a random image every time it's clicked? It's not actually > clear what you wanted here because there isn't any example code to go on. > >> Should the script call some method like redistribute() on the host upon >> click? But then, since the element needs to pick a child uniformly random, >> it probably needs to keep track of the number of children to be distributed >> and return true exactly when that node was passed into the callback. That’s >> an extremely cumbersome API at least for my use case. > > I have the sense that if you produced example code you'd be able to make a > better guess about what's onerous and what isn't. As it is, we're debating > hypotheticals. > > Here's a version of your component based on my proposal. I don't feel it's > particularly cumbersome in context: > > var log = console.log.bind(console); > var randomInt = function(min, max) { > return Math.floor(Math.random() * (max - min + 1)) + min; > } > > var rip = Object.create(HTMLElement.prototype); > rip.distributeCallback = function(children) { > while(this.shadowRoot.children.length) { > this.shadowRoot.remove(this.shadowRoot.children[0]); > } > var i = randomInt(0, children.length-1); > this.shadowRoot.add(children[i]); > }; > var RandomImage = document.registerElement( > "random-image", { prototype: rip }); > > > Obviously, the element needs to add a click handler to call > distributeCallback() manually with this.children, but I don't see how that's > hard.
And what exactly is the point of having this callback? Are UAs supposed to call it automatically in some situations? If so, when? - R. Niwa