-
Notifications
You must be signed in to change notification settings - Fork 27
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
appendChild won't work with Polymer 1.0 "shady DOM" #45
Comments
Okay, well it seems like a matter of changing all calls to DOM fn's to use |
Yes, that sounds right. |
So, in general I've tried to design this system to be as pluggable as possible. If all native DOM calls need to be replaced, we just isolate them by default and then "patch" them if needed. Also, check out the new DOM implementation a little - everything is now based on |
A potential issue with patching DOM calls might occur if you were using more than one Web Components-ish library. I don't know that anyone else is doing something like Polymer shady-DOM right now, but it seems likely to pop up, and then we'd need a way to dispatch based on the library used to create the element. Does IVirtualElement solve this? Could we perhaps render the correct IVirtualElement implementation based on a tag namespace or prefix? |
Yes, see: On Wed, Jun 3, 2015 at 1:28 PM, Dave Dixon [email protected] wrote:
|
Do you see any need to dispatch on non-prefixed tags? dom-element could On Wed, Jun 3, 2015 at 1:31 PM, Aaron Craelius [email protected]
|
Let me play with it. My first impression is that |
Okay. Probably because a custom element can use it's own "as-velem" for On Wed, Jun 3, 2015 at 2:16 PM, Dave Dixon [email protected] wrote:
|
Looking at the implementation of DOMElement as a starting point. It seems that PolymerDOMElement would share most of that implementation. Could we instead make the DOM API an argument to DOMElement? Or maybe that's what you meant by patching DOM calls. |
Yeah, we could take either approach. What do you think is better? I don't FYI, added some doc strings here: On Wed, Jun 3, 2015 at 2:30 PM, Dave Dixon [email protected] wrote:
|
Due to other issues that I am currently working through, I will probably decide to pass a "native dom wrapper" through the whole tree so this may provide a way to deal with this. |
From what I can tell from their docs and code, that seems like it will be the key piece for Polymer 1.0. |
So regarding Polymer's manipulation of DOM elements. If freactive uses Polymer's shady DOM API, wouldn't the DOM appear unchanged to freactive? I think everything would work fine if this were the case. The only real issue I can see would potentially be with using freactive's element sequences - in that if other DOM elements are inserted, then the ordering of elements could maybe be messed up. But again, if shady DOM hides all this manipulation, there probably wouldn't be an issue... |
I guess the key question is whether freactive is going to expect things to maintain a specific place in the DOM, i.e. in the When I get there with Cereus, I'll use the freactive interfaces for moving nodes, and that should keep things consistent. |
This implementation always holds its own references to nodes and keeps track of where it has placed nodes in the DOM. It would care if a list of children were moved if the reactive collection binding is being used because now things won't get inserted into the right places. But my understanding of Polymer's DOM API is that it would make it appear that things are the same place freactive put them, right? If that's the case then it wouldn't care what's going on in the final DOM. |
Agreed. |
The implementation for moving around nodes is here: https://github.com/aaronc/freactive/blob/develop/src/freactive/ui_common.cljs#L210-L234. This stuff is still sort of a work in progress, but the main idea is that this API would be used for managing movement: https://github.com/aaronc/freactive.core/blob/master/src/clojure/freactive/core.cljc#L877-L912 and would more or less expect that one projection source is managing a single projection target. Yes, it would be possible to move nodes between projection sources (for say drag and drop) if they knew how to do that. |
Looks good. Presumably, when I want to do something similar as Polymer, moving things as rendered in "logical" DOM to another place in the actual DOM, I would implement IVirtualElement to ensure that the parent gets notified when the logical child DOM changes. BTW, I verified freactive works find doing mount!/unmount! on the native shadow root. So you're future-proof ;-) |
So I think I have a relevant case for projection. The Polymer |
You could make your own custom virtual element that inserts it where you
|
I guess this case is actually covered if we have the ability to specify the DOM API implementation. Maybe a nice-to-have for this would be the ability to register the DOM API implementation by tag name. |
Well, I'm not sure it would be covered by the DOM API - does Polymer take On Thu, Jun 11, 2015 at 9:48 AM, Dave Dixon [email protected]
|
I was going to provide my own implementation of the DOM API which did the magic for the insert/append cases, otherwise called through to Polymer's DOM API. But thinking about it, the semantics might get sticky on how you associate a particular DOM API implementation with a particular tag, e.g. does it only apply to operations involving the element itself, or the element and it's children? So IVirtualElement is probably clearer. Would For the Polymer case, I was thinking |
Yeah, there are many different ways it could be done. One way is to have On Thu, Jun 11, 2015 at 12:41 PM, Dave Dixon [email protected]
|
Ok, I think I'm starting to get it. Seems like implementing IVirtualElement is for the case where you want to completely take over the DOM management from that point forward, maybe if you were using something like React Material UI. The case of HTML custom elements is more light weight. A Polymer element needs to push in the DOM API, but otherwise could be handled by the |
Yeah implementing We could have: (defn as-velem* [native-api]
(fn as-velem [x]
...))
(def as-velem (velem* native-dom))
(def as-velem-polymer (velem* polymer-dom)) |
With this approach all child elements would use the same |
That should do it. |
I'm at a good spot in my project where I could tackle this, if you want some help. |
That would be great. I will try to send a few notes later today on a
|
So this is what I've had in mind for a native API: (defprotocol INativeAPI
(native-queue [this f] "Queues the no-arg fn f to be run in the application thread of this UI platform")
(native-create-element [this elem-ns elem-name])
(native-create-text-node [this text])
(native-bind-attrs! [this elem attr-map]
"Binds a map of attributes to the element and returns an fn (or something that implements IFn) that when called with a single argument representing the new map of attributes will rebind the attributes on this element. This fn object map also have a plain javascript dispose member fn that will be called when the element is disposed to perform any clean-up.")
(native-insert [this parent elem before?])
(native-replace [this new-elem old-elem])
(native-remove [this elem])
(native-element? [this x])
(native-text-node? [this x])
(native-set-text-node! [this node text])) The most complex thing here may be attribute binding. The code for this now is actually designed to be as generic as possible and it should be pretty straight-forward to adapt it - if it's not evident how this works let me know. With this sort of API, it may actually be possible to work with other UI frameworks (for instance javascript bindings for iOS or Android native components). In that case DOMElement would become just Element as it would be pretty generic. One other consideration for Polymer is the .flush fn - we can create a hook for this in the render loop if it makes sense. |
Good start, I like how this is shaping up. I think we probably also need functions to get (and set, where applicable) DOM properties like Is there a reason to do Do we want pre and post render hooks? I have a couple of ideas for the future that I think would use this, particularly using cassowary.js for constraint-based layout. I did a quick and dirty attempt earlier today, diff here: develop...allgress:develop. To avoid passing the DOM API through all of the relevant functions, I followed Polymer's example and stuck it on the element as a property, and registered element create APIs which in turn would place the appropriate DOM API on the element. There's a little subtlety here, because in order to get (defn- polymer-dom-api
[node]
(if-let [dom-api (.-__domApi node)]
(let [append-child (.-appendChild dom-api)]
(set! (.-appendChild dom-api)
(fn [child]
(set! (.-freactive-dom-api child) polymer-dom-api)
(.call append-child dom-api child)))
dom-api)
node))
(def create-polymer-element
(reify
ui/ICreateElement
(createElement [this tag]
(let [el (.createElement js/document tag)]
(set! (.-freactive-dom-api el) polymer-dom-api)
el))
(createElementNS [this tag ns-uri]
(let [el (.createElementNS js/document tag ns-uri)]
(set! (.-freactive-dom-api el) polymer-dom-api))))) Registration was then done via (register-create-element! :paper-button util/create-polymer-element) Did some testing and verified the works correctly with |
Clarifying my question on Also, I think the [:paper-button
[:span "I'm a button"]] The |
After digging through the code, I'm going to guess at the answer to the |
First try using INativeAPI now pushed to the Allgress fork of freactive. Works as far as I can tell, including our reasonably non-trivial app running Polymer 1.0. Still some details to be ironed out, I think, particularly around attribute binding. I've only implement get/set/remove attribute functions on INativeAPI, and am using your existing attribute binding code. For normal attributes, I would think this code works for all browser-based DOM APIs. Style and event attributes are currently unwrapped, and Polymer at least doesn't seem to care about these. |
So, I'm not quite sure why the |
Currently I have it so each element gets a reference to its native API, e.g. in the example above the Theoretically, I think Polymer's DOM API could be used everywhere, but there will be some performance penalty, which is why they kept it separate in 1.0 rather than just overwriting the native definitions as was done in Polymer 0.5. |
See this discussion.
Bottom line: for performance reasons, Polymer 1.0 gives the option of not polyfilling the full shadow DOM spec by overwriting DOM API's like
appendChild
. For at least some Polymer elements, we need to execute slightly different code to ensure that things render correctly, allowing the element to be notified when its content has changed. I played with this a bit in my local copy of freactive, and was able to make it work by adding aregister-append-child
function infreactive.dom
:And then calling it like this:
The design maybe needs some more thought. Seems onerous to have to register for every element, maybe makes more sense to provide a predicate function. Probably could streamline performance a bit as well. I assume
insertBefore
has the same issue, but haven't tested.The text was updated successfully, but these errors were encountered: