Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
I just saw Dimitri's reference to my “Filling slots in shadow” blog posts from a while back, so I thought I’d follow up with the experiences I’ve had I wrote it. First, I remain convinced that it will be very helpful for Shadow DOM to provide a feature like this that allows for parent and child classes to cooperatively contribute elements to the same component. This is a key feature that enables well-factored user interface components. I started a company earlier this year that is building a site entirely on web components, and we’ve already hit the limitations of what’s possible without this feature. Second, my colleagues and I found the pre-existing proposal to let authors reproject content into shadow to be an elegant solution to this problem. During the brief window when this feature was available in Chrome Canary, we quickly made use of it. It felt like a very natural extension of the Shadow DOM tree composition concepts we had already learned. That is, in our experience, the conceptual load introduced by this feature was low. To the extent the developer experience weighs in any decision here, we were completely fine with the approach of placing content insertion points inside shadow elements. Third, we feel a modest amount of impatience to see this feature implemented in Chrome again, and for other browsers to adopt this feature as it existed in Chrome. It was meeting our needs, and seeing it (even temporarily) removed felt like a step backwards. We’re concerned that lack of a solution here will discourage people from applying subclassing as a means to factor user interface behavior into parent/child component class relationships. Finally, even given all the above, we view it as more important that all the mainstream browsers implement the more fundamental aspects of Shadow DOM with all due speed. Our company and its customers are entirely dependent upon web components that use Shadow DOM, and while we can rely upon the Polymer library for cross-browser compatibility, there is a considerable different in performance between native and polyfilled Shadow DOM features. We test on a variety of browsers and devices, and while we can run on Mobile Safari or IE, performance on those platforms is barely acceptable. We would rather see the basics of Shadow DOM be native now, and live without defined semantics for parent/child contributions to shadow trees, than wait indefinitely for that feature before seeing native implementations of the basics. I feel like something of an industry outsider in discussions related to web standards, and appreciate the range of scenarios those working on standards must consider as they debate proposals for changes. I trust the smart folks at Apple, Google, and elsewhere to make the right decision here. All I can offer here is my company's own practical experience in this domain. For our part, we would be very happy to see a solution adopted in the standard that follows the general lines of allowing reprojection of content into shadow elements, but in the meantime are primarily concerned with seeing native Shadow DOM implementations across all browsers. –Jan
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
On Sun, Apr 27, 2014 at 2:36 PM, Ryosuke Niwa rn...@apple.com wrote: On Apr 22, 2014, at 10:46 AM, Dimitri Glazkov dglaz...@chromium.org wrote: BTW, here's a jsbin that implements yield/transclude using existing Shadow DOM plumbing: http://jsbin.com/pacim/1/edit Thanks for an example. That indeed polyfills the API we're proposing in terms of the currently spec'ed API. Great! One important aspect of our proposal is to avoid having to rely on shadowRoot property on HTMLElement, which wouldn't exist in Type III encapsulation. Note, that the access to element's shadowRoot happens from the element's own implementation of a method. If I were a developer of a framework that implements the idea you suggested, I would do something like this: http://jsbin.com/pacim/4/edit On Apr 22, 2014, at 11:06 AM, Dimitri Glazkov dglaz...@chromium.org wrote: Here's a jsbin that uses the shadow-as-function syntax and does the same thing: http://jsbin.com/peqoz/2/edit This one doesn't quite work as intended in that the insertion points in MyCardElement's shadow DOM could grab nodes from the light DOM. Help me understand what you mean here. Which nodes will these insertion points grab? Would love an example. :DG
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
Possibly relevant to the conversation: Jan Miksovsky (cc'd) had been thinking in this problem space for a while, and has a couple of great blog posts on the topic: http://blog.quickui.org/2013/11/08/filling-slots-in-shadow/ http://blog.quickui.org/2013/06/11/puzzle-define-html-custom-element-subclasses-that-can-fill-in-base-class-insertion-points/ :DG
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
On Apr 22, 2014, at 10:22 AM, Dimitri Glazkov dglaz...@chromium.org wrote: On Thu, Apr 17, 2014 at 2:42 AM, Ryosuke Niwa rn...@apple.com wrote: Review: Template Inheritance in the Current Specification In the current specification, a super class doesn't define any hooks for subclasses. Instead, it defines insertion points into which nodes from the original DOM (light DOM) is inserted, and then subclasses use shadow element to replace elements that get distributed into superclass's insertion points. Consider my-card element used as follows: my-card span class=nameRyosuke Niwa/span span class=emailrn...@apple.com/span /my-card Suppose this element's shadow DOM looks like this: Name: content select=.name/content Email: content select=.email/content Then in the composed tree, the first span is distributed into the first content element and the second span is distributed into the second content element as follows: my-card !-- shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameRyosuke Niwa/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailrn...@apple.com/span !-- distribution end -- /content !-- shadow root end -- /my-card If I had my-webkitten-card that always as WebKitten as a name that inherits from my-card, its shadow DOM may look like this: shadow span class=nameWebKitten/span content/content span class=emailkit...@webkit.org/span /shadow If I had an instance of my-webkitten-card as follows: my-webkitten-card span class=nameRyosuke Niwa/span span class=emailrn...@webkit.org/span /my-webkitten-card Then its composed tree will look like this: my-webkitten-card !-- my-webkitten-card's shadow root begin -- shadow !-- my-card's shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameWebKitten/span span class=nameRyosuke Niwa/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailrn...@webkit.org/span span class=emailkit...@webkit.org/span !-- distribution end -- /content !-- my-card's shadow root end -- /shadow !-- my-webkitten-card's shadow root end -- /my-webkitten-card Here, my-card's shadow DOM was inserted into where the shadow element existed in my-webkitten-card's shadow DOM, and the insertion points inside my-card's shadow DOM got nodes distributed from shadow element's children including nodes inside content element. If we didn't have the content element inside my-webkitten-card with name and email classes, then we would only see WebKitten and kit...@webkit.org distributed into my-card's insertion points as in: my-webkitten-card !-- my-webkitten-card's shadow root begin -- shadow !-- my-card's shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameWebKitten/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailkit...@webkit.org/span !-- distribution end -- /content !-- my-card's shadow root end -- /shadow !-- my-webkitten-card's shadow root end -- /my-webkitten-card Separating Transclusion Mechanisms for Inheritance and Data Binding The current model mixes data binding and inheritance if we consider distributing nodes from the light DOM as a form of data binding. Namely, distributing nodes from my-card's or my-webkitten-card's light DOM is data binding where the data model is DOM whereas distributing nodes from my-webkitten-card's shadow element into my-card's insertion points is an inheritance hook. Furthermore, the transclusion mechanism for inheritance happens backwards. Instead of a superclass defining a transclusion points for its subclasses to use, the subclasses are overriding the meaning of insertion points in the superclass by injecting nodes. This is how existing JS libraries and frameworks do template inheritance. For example, the following two JS template libraries that support inheritance both allow superclass template to define named blocks that could be overridden by subclass templates: http://paularmstrong.github.io/swig/docs/#inheritance http://jlongster.github.io/nunjucks/templating.html#template-inheritance An example from Nunjucks: If we have a template parent.html that looks like this: {% block header %} This is the default content {% endblock %} section class=left {% block left %}{% endblock %} /section section class=right {% block right %} This is more content {% endblock %}
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
On Apr 22, 2014, at 10:13 AM, Dimitri Glazkov dglaz...@chromium.org wrote: On Thu, Apr 17, 2014 at 2:42 AM, Ryosuke Niwa rn...@apple.com wrote: Review: Template Inheritance in the Current Specification In the current specification, a super class doesn't define any hooks for subclasses. Instead, it defines insertion points into which nodes from the original DOM (light DOM) is inserted, and then subclasses use shadow element to replace elements that get distributed into superclass's insertion points. Consider my-card element used as follows: my-card span class=nameRyosuke Niwa/span span class=emailrn...@apple.com/span /my-card Suppose this element's shadow DOM looks like this: Name: content select=.name/content Email: content select=.email/content Then in the composed tree, the first span is distributed into the first content element and the second span is distributed into the second content element as follows: my-card !-- shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameRyosuke Niwa/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailrn...@apple.com/span !-- distribution end -- /content !-- shadow root end -- /my-card If I had my-webkitten-card that always as WebKitten as a name that inherits from my-card, its shadow DOM may look like this: shadow span class=nameWebKitten/span content/content span class=emailkit...@webkit.org/span /shadow If I had an instance of my-webkitten-card as follows: my-webkitten-card span class=nameRyosuke Niwa/span span class=emailrn...@webkit.org/span /my-webkitten-card Then its composed tree will look like this: my-webkitten-card !-- my-webkitten-card's shadow root begin -- shadow !-- my-card's shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameWebKitten/span span class=nameRyosuke Niwa/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailrn...@webkit.org/span span class=emailkit...@webkit.org/span !-- distribution end -- /content !-- my-card's shadow root end -- /shadow !-- my-webkitten-card's shadow root end -- /my-webkitten-card Here, my-card's shadow DOM was inserted into where the shadow element existed in my-webkitten-card's shadow DOM, and the insertion points inside my-card's shadow DOM got nodes distributed from shadow element's children including nodes inside content element. If we didn't have the content element inside my-webkitten-card with name and email classes, then we would only see WebKitten and kit...@webkit.org distributed into my-card's insertion points as in: my-webkitten-card !-- my-webkitten-card's shadow root begin -- shadow !-- my-card's shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameWebKitten/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailkit...@webkit.org/span !-- distribution end -- /content !-- my-card's shadow root end -- /shadow !-- my-webkitten-card's shadow root end -- /my-webkitten-card Can you help me understand the problems you illustrated with the examples better? What's the desired behavior? Where does the problem arise? This is both for my-webkitten-card and for random-element examples. I would love to have these to start chewing on them. Sorry, maybe my description wasn't clear. This example doesn't demonstrate any issues. I was simply describing what the current specification does. - R. Niwa
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
On Apr 22, 2014, at 10:46 AM, Dimitri Glazkov dglaz...@chromium.org wrote: BTW, here's a jsbin that implements yield/transclude using existing Shadow DOM plumbing: http://jsbin.com/pacim/1/edit Thanks for an example. That indeed polyfills the API we're proposing in terms of the currently spec'ed API. One important aspect of our proposal is to avoid having to rely on shadowRoot property on HTMLElement, which wouldn't exist in Type III encapsulation. On Apr 22, 2014, at 11:06 AM, Dimitri Glazkov dglaz...@chromium.org wrote: Here's a jsbin that uses the shadow-as-function syntax and does the same thing: http://jsbin.com/peqoz/2/edit This one doesn't quite work as intended in that the insertion points in MyCardElement's shadow DOM could grab nodes from the light DOM. - R. Niwa
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
On Thu, Apr 17, 2014 at 2:42 AM, Ryosuke Niwa rn...@apple.com wrote: *Review: Template Inheritance in the Current Specification* In the current specification, a super class doesn't define any hooks for subclasses. Instead, it defines insertion points into which nodes from the original DOM (light DOM) is inserted, and then subclasses use shadow element to replace elements that get distributed into superclass's insertion points. Consider my-card element used as follows: my-card span class=nameRyosuke Niwa/span span class=emailrn...@apple.com/span /my-card Suppose this element's shadow DOM looks like this: Name: content select=.name/content Email: content select=.email/content Then in the composed tree, the first span is distributed into the first content element and the second span is distributed into the second content element as follows: my-card !-- shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameRyosuke Niwa/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailrn...@apple.com/span !-- distribution end -- /content !-- shadow root end -- /my-card If I had my-webkitten-card that always as WebKitten as a name that inherits from my-card, its shadow DOM may look like this: shadow span class=nameWebKitten/span content/content span class=emailkit...@webkit.org/span /shadow If I had an instance of my-webkitten-card as follows: my-webkitten-card span class=nameRyosuke Niwa/span span class=emailrn...@webkit.org/span /my-webkitten-card Then its composed tree will look like this: my-webkitten-card !-- my-webkitten-card's shadow root begin -- shadow !-- my-card's shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameWebKitten/span span class=nameRyosuke Niwa/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailrn...@webkit.org/span span class=emailkit...@webkit.org/span !-- distribution end -- /content !-- my-card's shadow root end -- /shadow !-- my-webkitten-card's shadow root end -- /my-webkitten-card Here, my-card's shadow DOM was inserted into where the shadow element existed in my-webkitten-card's shadow DOM, and the insertion points inside my-card's shadow DOM got nodes distributed from shadow element's children including nodes inside content element. If we didn't have the content element inside my-webkitten-card with name and email classes, then we would only see WebKitten and kit...@webkit.org distributed into my-card's insertion points as in: my-webkitten-card !-- my-webkitten-card's shadow root begin -- shadow !-- my-card's shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameWebKitten/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailkit...@webkit.org/span !-- distribution end -- /content !-- my-card's shadow root end -- /shadow !-- my-webkitten-card's shadow root end -- /my-webkitten-card Can you help me understand the problems you illustrated with the examples better? What's the desired behavior? Where does the problem arise? This is both for my-webkitten-card and for random-element examples. I would love to have these to start chewing on them. :DG
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
On Thu, Apr 17, 2014 at 2:42 AM, Ryosuke Niwa rn...@apple.com wrote: *Review: Template Inheritance in the Current Specification* In the current specification, a super class doesn't define any hooks for subclasses. Instead, it defines insertion points into which nodes from the original DOM (light DOM) is inserted, and then subclasses use shadow element to replace elements that get distributed into superclass's insertion points. Consider my-card element used as follows: my-card span class=nameRyosuke Niwa/span span class=emailrn...@apple.com/span /my-card Suppose this element's shadow DOM looks like this: Name: content select=.name/content Email: content select=.email/content Then in the composed tree, the first span is distributed into the first content element and the second span is distributed into the second content element as follows: my-card !-- shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameRyosuke Niwa/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailrn...@apple.com/span !-- distribution end -- /content !-- shadow root end -- /my-card If I had my-webkitten-card that always as WebKitten as a name that inherits from my-card, its shadow DOM may look like this: shadow span class=nameWebKitten/span content/content span class=emailkit...@webkit.org/span /shadow If I had an instance of my-webkitten-card as follows: my-webkitten-card span class=nameRyosuke Niwa/span span class=emailrn...@webkit.org/span /my-webkitten-card Then its composed tree will look like this: my-webkitten-card !-- my-webkitten-card's shadow root begin -- shadow !-- my-card's shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameWebKitten/span span class=nameRyosuke Niwa/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailrn...@webkit.org/span span class=emailkit...@webkit.org/span !-- distribution end -- /content !-- my-card's shadow root end -- /shadow !-- my-webkitten-card's shadow root end -- /my-webkitten-card Here, my-card's shadow DOM was inserted into where the shadow element existed in my-webkitten-card's shadow DOM, and the insertion points inside my-card's shadow DOM got nodes distributed from shadow element's children including nodes inside content element. If we didn't have the content element inside my-webkitten-card with name and email classes, then we would only see WebKitten and kit...@webkit.org distributed into my-card's insertion points as in: my-webkitten-card !-- my-webkitten-card's shadow root begin -- shadow !-- my-card's shadow root begin -- Name: content select=.name !-- distribution begin -- span class=nameWebKitten/span !-- distribution end -- /content Email: content select=.email !-- distribution begin -- span class=emailkit...@webkit.org/span !-- distribution end -- /content !-- my-card's shadow root end -- /shadow !-- my-webkitten-card's shadow root end -- /my-webkitten-card *Separating Transclusion Mechanisms for Inheritance and Data Binding* The current model mixes data binding and inheritance if we consider distributing nodes from the light DOM as a form of data binding. Namely, distributing nodes from my-card's or my-webkitten-card's light DOM is data binding where the data model is DOM whereas distributing nodes from my-webkitten-card's shadow element into my-card's insertion points is an inheritance hook. Furthermore, the transclusion mechanism for inheritance happens backwards. Instead of a superclass defining a transclusion points for its subclasses to use, the subclasses are overriding the meaning of insertion points in the superclass by injecting nodes. This is how existing JS libraries and frameworks do template inheritance. For example, the following two JS template libraries that support inheritance both allow superclass template to define named blocks that could be overridden by subclass templates: http://paularmstrong.github.io/swig/docs/#inheritance http://jlongster.github.io/nunjucks/templating.html#template-inheritance An example from Nunjucks: If we have a template parent.html that looks like this: {% block header %}This is the default content{% endblock %} section class=left {% block left %}{% endblock %}/section section class=right {% block right %} This is more content {% endblock %}/section And we render this template: {% extends parent.html %} {% block left %}This is the left side!{% endblock %} {% block right
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
BTW, here's a jsbin that implements yield/transclude using existing Shadow DOM plumbing: http://jsbin.com/pacim/1/edit :DG
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
Here's a jsbin that uses the shadow-as-function syntax and does the same thing: http://jsbin.com/peqoz/2/edit :DG On Tue, Apr 22, 2014 at 10:46 AM, Dimitri Glazkov dglaz...@chromium.orgwrote: BTW, here's a jsbin that implements yield/transclude using existing Shadow DOM plumbing: http://jsbin.com/pacim/1/edit :DG
Re: Separating Transclusion Mechanisms for Inheritance and Data Binding
*claps for sharing JSBin* Would love to see more of this on public-webapps when possible :) On Tue, Apr 22, 2014 at 10:46 AM, Dimitri Glazkov dglaz...@chromium.orgwrote: BTW, here's a jsbin that implements yield/transclude using existing Shadow DOM plumbing: http://jsbin.com/pacim/1/edit :DG