Bug Tracker

Ticket #13342 (closed feature: plugin)

Opened 20 months ago

Last modified 2 months ago

Support setting $.html() on shadow roots

Reported by: jan@… Owned by:
Priority: low Milestone: None
Component: manipulation Version: git
Keywords: Cc:
Blocking: Blocked by:

Description

The forthcoming public support for Shadow DOM components ( https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html) in Google Chrome opens up the possibility of creating reusable web UI components. To date, nearly all Shadow DOM examples use the native DOM API, but it should be possible to use jQuery for this purpose. As it turns out, jQuery is already very nearly able to manipulate the contents of a Web Component. A small change to core.js will enable proper support.

Example:  http://jsfiddle.net/Jan_Miksovsky/y9hSm/ This fiddle creates a very basic Web Component that adds some simple text to its content. When viewed in a production browser, the fiddle will display a simple list of names like "Ann". When viewed in a Shadow DOM-capable browser like Google Canary (or, I believe, beta channel), the fiddle will display additional text around each name: "Hello, Ann."

This is done by creating a shadow root, then setting its innerHTML to "Hello <content></content>". It's not possible in jQuery 1.9 to directly manipulate the innerHTML of a shadow root using html(). The issue is that core.js checks to make sure that the nodeType of an element being manipulated by html() is of nodeType 1 (element). However, a shadow root is actually of nodeType 11 (document fragment). Unlike normal document fragments, shadow roots expose an innerHTML property just like regular HTMLElement nodes.

I have a small fix and accompanying unit test for this, and am filing this bug in preparation for a pull request. (This fix is applied in the private jquery.js used in the fiddle.) This fix updates the check in core.js to permit shadow roots. With that, html() can directly manipulate the innerHTML of shadow roots — that is, Web Component content — correctly. (To see the effects of such manipulation in Google Canary, be sure to turn on "Show Shadow DOM" in the Chrome debugging tools.)

Shadow DOM support will likely land in production Chrome in the not-too-distant future, followed by Mozilla later this year, so this seems like a proactive time to make this fix (or something similar that accomplishes the same objective). This fix doesn't require Shadow DOM support, and in fact doesn’t directly test for Shadow DOM; it should be backward compatible with other browsers, and safe to roll out at any point, even before Shadow DOM-capable browsers become widespread.

Change History

comment:1 Changed 20 months ago by dmethvin

I have a feeling there is more to shadow DOM support than just allowing .innerHTML on document fragments but I haven't looked at the spec in detail. Are you experimenting with shadow DOM components yourself? It would be great to support this to a reasonable extent when it lands, but I wonder if it even makes sense to support in 1.9 vs. just putting it in 2.0. What kind of fallback would a web page/app/site use if shadow DOM wasn't there?

comment:2 Changed 20 months ago by anonymous

I've been tracking Shadow DOM development for the better part of a year, and recently it's advanced to the point where I'd like to start supporting Shadow DOM in a jQuery-based web UI framework I maintain ( http://quickui.org). While the WebKit team is framework-agnostic, and does all documentation in the standard DOM API, I prefer to do all DOM manipulation through jQuery.

This bug is meant to address the first, most basic issue I ran into in doing Shadow DOM work with jQuery. Fixing this is likely just a start; there will likely be many more Shadow DOM-related issues that come up as the technology finds more users.

As for a fallback, Google is working with several parties to create polyfills for Web Components overall. Given the nature of Shadow DOM, it's really impossible to provide a complete backwards-compatible polyfill, although one could certainly address some of its features in JavaScript. That is, in fact, the work I'm attempting to pursue in my UI framework: give jQuery developers a way to create UI components that render in the Shadow DOM on browsers that support that technology, and fall back to widget-style UI components in older browsers.

This fix is intended to enable that work -- and that of other people who want to start creating Web Components in JavaScript and jQuery rather than using Google's proposed HTML <element> format for Web Components.

comment:4 Changed 20 months ago by paul.irish

Seems legit. I'm also interested in where else jQuery needs any fixes for Shadow DOM & WC, but this particular case (as discussed on the PR) could be making a general improvement while simultaneously getting shadow dom compat.

comment:5 Changed 20 months ago by dmethvin

I'd really like to see some examples of how this would be used. The person passing in the DOM fragment knows what it is and could use .innerHTML directly, right? And if this would end up being wrapped in some sort of jQuery plugin or other component that might be the right place to put this sort of knowledge. Once you're *inside* the component I can see that using jQuery all normal-like.

Edit: Also, this is implying that $() now understands shadow roots and supports some subset of jQuery methods on them. Can I use .append(), .appendTo(), .replaceWith() for example? You may not want to do that but I can bet that if we let people wrap it in a jQuery then people will expect it to behave as consistently as possible.

Last edited 20 months ago by dmethvin (previous) (diff)

comment:6 Changed 20 months ago by jan@…

I think the first question, "Why would you want to invoke jQuery on a shadow root?", is effectively the same as the question, "Why would you want to invoke jQuery on document.body?" E.g., you might want to search within a component's Shadow DOM for all elements that match a selector in preparation for some global manipulation. While a component developer could work around this as you suggest (by setting innerHTML), that's just as cumbersome to a jQuery dev as needing to work with <body> specially.

Of more concern are situations in which the dev is invoking code outside their control. Suppose a jQuery widget exists that performs some useful function, and the widget's create() call invokes this.element.html(). That widget (and any other link it) will fail to perform the expected functions on a shadow root, and the code is outside the dev's control. A common case of this might be wanting to wrap a jQuery UI widget as a web component to encapsulate it and puts its contents out of reach of the widget's host. For an example of a jQuery UI widget wrapped in a component, see  http://jsfiddle.net/Jan_Miksovsky/y2agp/ (in Google Canary).

The dev is forced to work around the problem by placing a placeholder div (or other element) within their shadow root. The extra layer may not seem bad, but if you have a component-centric app architecture, you could easily end up with many hundreds of components on a page, and the extra div per component adds a lot of clutter.

Your point about the general case of other jQuery functions is well taken. My own framework only needs .html(), but it's reasonable to expect that other people will assume other functions work as well. As it turns out, a quick check shows that .append() does indeed work as expected on a shadow root -- but something like .css() does not, because a shadow root doesn't have a style property. (Although upon reflection, if it has an innerHTML property, there's a case to be made that it should have style too. I'll ask about that.)

I can see that, before tackling this piecemeal, it might be helpful for the jQuery team to review a more complete assessment of what the current and expected behaviors of all jQuery functions are when applied to a Shadow DOM root. If you like, I can take that on, i.e., see what works as expected and what doesn't, compile the results, and submit a recommendation as to where jQuery could fix things. (In some cases, where the current spec doesn't support the expected behavior, the result might be useful feedback to the folks working on the Web Components spec.)

Would that help?

comment:7 Changed 20 months ago by dmethvin

  • Priority changed from undecided to low
  • Status changed from new to open
  • Component changed from unfiled to manipulation

I agree that it would be more helpful for a holistic look at the issue rather than trying to tackle it piecemeal. If someone familiar with where the proposal stands and where it is going would like to take a look that would be great.

Although upon reflection, if it has an innerHTML property, there's a case to be made that it should have style too.

That's one reason why I think it's possibly too early to land anything here. It would be pretty easy to encapsulate this in a plugin initially, for example by duck-punching .html() and catching the shadow root case.

There are some comments on  https://github.com/jquery/jquery/pull/1149 but I'll close that and let's try to keep discussion here until we decide what to do.

comment:8 Changed 20 months ago by scott.gonzalez

There's a discussion in public-webapps about making shadow root an element:  http://lists.w3.org/Archives/Public/public-webapps/2013JanMar/0356.html

comment:9 Changed 20 months ago by angelinafabbro@…

Just chiming in with a few points to agree/disagree:

  1. It's probably a little early to incorporate this feature into jQuery specifically because the spec is still not entirely finalized. Piecemeal implementation/partial implementation of shadow root manipulation without <template> and custom elements is not as exciting as with them. Given that jQuery is a library that designers are partial to for ease of use (as many designers are not necessarily web developers, but do code some) I'd hope to have the declarative instantiation of shadow roots through tags otherwise I think it may be ignored for a while. (Except by those of us who think web components are super cool and may be more advanced users of JS.)
  1. Cross-browser support is still sorely lacking, and cross-browser support that jQuery seems to take very seriously. Browsers are holding out for the spec to be finalized. So, I think if you were to do this and do it right the jQuery team may be writing an awful lot of polyfill and fallback code if you were to implement now. jQuery probably should to to avoid undoing/redoing implementation. I mean, iteration is a good strategy but not with a moving target that has to do with DOM & DOM fragment manipulation.

but -

I really like the idea of getting these features into a general purpose library like jQuery as soon as it makes a bit more sense to do so. I think this will aid in propagating the idea of building better web apps with encapsulation. jQuery has a lot of reach! This would be a good thing for the web.

Cheers,

  • Angelina Fabbro

comment:10 Changed 17 months ago by dmethvin

  • Status changed from open to closed
  • Resolution set to plugin

This is a great discussion of the current state of Shadow DOM but I think any implementation should start as a plugin so we can evaluate its utility and stability. So let's close the ticket and watch the spec evolve for now. There is no risk of anyone letting us forget about this.

comment:11 Changed 2 months ago by balupton

Shadow DOM is currently broken for jQuery:

Fix:  https://github.com/jquery/jquery/pull/1631 Show case:  http://jsbin.com/lirejo/edit

Note: See TracTickets for help on using tickets.