-
Notifications
You must be signed in to change notification settings - Fork 64
Adds attachShadow({shadyUpgradeFragment: documentFragment})
#316
Conversation
* As an optimization for creation and initial population of a ShadowRoot, adds the optional `upgrade` static method. Note, the `fragment` dom must already have any necessary style scoping applied. * Also removes initial default property sets from `ShadyData` and `ShadyRoot` which is empirically slightly faster. * slightly optimizes capturing logical data by not accessing `childNodes`
This replaces but is the same as `ShadowRoot.upgrade`. Prefer putting bespoke API on non-standard object. Also adds some slight type refinement.
Remove`.only` that was mistakenly skipping tests. Slight re-organization of a few tests
Since `appendChild` is not used, the custom elements patch that calls `connectedCallback` will not run. Therefore, do not use this when custom elements are being polyfilled.
Performing `appendChild` with a documentFragment with a patched prototype appears to blow up IE.
Object.defineProperty(window['CustomElementRegistry'].prototype, 'define', { | ||
value: function(name, constructor) { | ||
//Object.defineProperty(window['CustomElementRegistry'].prototype, 'define', { | ||
window.customElements.define = function(name, constructor) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're keeping this change, let's add a comment for why... did something else patch the instance rather than the prototype?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, ShadyCSS patches this directly. Changing is path of least resistance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could change this though, correct? https://github.com/webcomponents/shadycss/blob/f377017e50f4d472a57ac224973f88b63a92f17e/src/style-placeholder.js#L54
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "fast" (tearoff) native shim also patches the instance. So nevermind, let's just align on instance patching for now.
src/link-nodes.js
Outdated
let c$ = node[utils.SHADY_PREFIX + 'childNodes']; | ||
for (let i=0; i < c$.length; i++) { | ||
linkNode(c$[i], container, ref_node); | ||
const first = node[utils.NATIVE_PREFIX + 'firstChild'] || null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is || null
required? Native should always return null
, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, removing.
src/link-nodes.js
Outdated
if (!root && nodeData.firstChild !== undefined) { | ||
return; | ||
} | ||
root = root || node; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confusing to call this root
here, it's really parent
. L110 could just be sd.parentNode = root || node
.
It might be good to call the root
argument adoptedRoot
or something like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to adoptedParent
@@ -307,7 +306,7 @@ export const NodePatches = utils.getOwnPropertyDescriptors({ | |||
}); | |||
} | |||
// if a slot is added, must render containing root. | |||
if (this.localName === 'slot' || slotsAdded.length) { | |||
if (slotsAdded.length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can this.localName === 'slot'
be removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was incorrect. This says "the node we're inserting into is a slot" and in this case a slot was not added. What it was meant to trap was node.localName
but this is not needed because the treeVisitor
checks node
.
src/shadydom.js
Outdated
@@ -82,9 +84,23 @@ if (utils.settings.inUse) { | |||
// access that requires Shadow DOM behavior to be proxied via `ShadyDOM.wrap`. | |||
'noPatch': utils.settings.noPatch, | |||
'nativeMethods': nativeMethods, | |||
'nativeTree': nativeTree | |||
'nativeTree': nativeTree, | |||
'upgrade': (fragment, host, options) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
upgrade
just sounds too much like the CE concept... can we just call it attachDom
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
ShadyDOM.upgrade(fragment, host, options)
ShadyDOM.attachShadow(fragment, host, options)
ShadyDOM.attachShadow(fragment, host, options)
ShadyDOM.attachDom(fragment, host, options)
src/utils.js
Outdated
settings.IS_IE = IS_IE; | ||
|
||
let _canUpgrade; | ||
// do not use `upgrade` when custom elements is polyfilled or on IE. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Elminiate the bail-out on CE polyfill by detecting shadowRoot.appendChild(shadowRoot)
and doing the attachDom
work there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IE blows up if we patch the documentFragment.
Optimizations were moved in situ: attachDOM
puts the fragment on the host and attachShadow
and appendChild
use that.
This requires making sure `attachShadow` and `appendChild` are called. The code now special cases inside of those patches.
* ensures a shadowRoot itself does not maintain a `__noInsertionPoint` flag after initial creation when ShadyDOM.attachDom is used. * adds small optimization to prevent text nodes from being unscoped
@@ -440,7 +440,7 @@ export function addEventListener(type, fnOrObj, optionsOrCapture) { | |||
const wrapperFn = function(e) { | |||
// Support `once` option. | |||
if (once) { | |||
this[utils.SHADY_PREFIX + 'removeEventListener'](type, fnOrObj, nativeEventOptions); | |||
this[utils.SHADY_PREFIX + 'removeEventListener'](type, fnOrObj, optionsOrCapture); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not following this change...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The nativeEventOptions
should only be passed to native methods. It's important to pass the actual options to the removeEventListener
so we can find the wrapper function to remove.
src/shadydom.js
Outdated
@@ -82,7 +84,16 @@ if (utils.settings.inUse) { | |||
// access that requires Shadow DOM behavior to be proxied via `ShadyDOM.wrap`. | |||
'noPatch': utils.settings.noPatch, | |||
'nativeMethods': nativeMethods, | |||
'nativeTree': nativeTree | |||
'nativeTree': nativeTree, | |||
'attachDom': (fragment, host, options) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: eliminate attachDom
and instead have attachShadow take another option, such that this pattern triggers the same logic:
this.attachShadow({mode: 'open', shadyUseFragment: fragment});
this.shadowRoot.appendChild(fragment);
ShadyDOM.attachDom(fragment, host, options)
attachShadow({shadyUpgradeFragment: documentFragment})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This API is being added as an optimization for creating and initially populating a ShadowRoot. Instead of:
it's now faster to do:
This removes the need to create an extra document fragment and optimizes the copying of nodes into the
shadowRoot
. Note that when this is used, the passed indocumentFragment
becomes theshadowRoot
.