mirror of
https://github.com/jlengrand/webcomponentsjs.git
synced 2026-03-10 08:51:22 +00:00
Ensure attached & detached occur for moves in the same turn.
Fixes #311
This commit is contained in:
@@ -27,35 +27,34 @@ var forDocumentTree = scope.forDocumentTree;
|
||||
// manage lifecycle on added node and it's subtree; upgrade the node and
|
||||
// entire subtree if necessary and process attached for the node and entire
|
||||
// subtree
|
||||
function addedNode(node) {
|
||||
return added(node) || addedSubtree(node);
|
||||
function addedNode(node, isAttached) {
|
||||
return added(node, isAttached) || addedSubtree(node, isAttached);
|
||||
}
|
||||
|
||||
// manage lifecycle on added node; upgrade if necessary and process attached
|
||||
function added(node) {
|
||||
if (scope.upgrade(node)) {
|
||||
function added(node, isAttached) {
|
||||
if (scope.upgrade(node, isAttached)) {
|
||||
// Return true to indicate
|
||||
return true;
|
||||
}
|
||||
attached(node);
|
||||
if (isAttached) {
|
||||
attached(node);
|
||||
}
|
||||
}
|
||||
|
||||
// manage lifecycle on added node's subtree only; allows the entire subtree
|
||||
// to upgrade if necessary and process attached
|
||||
function addedSubtree(node) {
|
||||
function addedSubtree(node, isAttached) {
|
||||
forSubtree(node, function(e) {
|
||||
if (added(e)) {
|
||||
if (added(e, isAttached)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function attachedNode(node) {
|
||||
attached(node);
|
||||
// only check subtree if node is actually in document
|
||||
if (inDocument(node)) {
|
||||
forSubtree(node, function(e) {
|
||||
attached(e);
|
||||
});
|
||||
attached(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,9 +103,8 @@ function _attached(element) {
|
||||
// track element for insertion if it's upgraded and cares about insertion
|
||||
if (element.__upgraded__ &&
|
||||
(element.attachedCallback || element.detachedCallback)) {
|
||||
// bail if the element is already marked as attached and proceed only
|
||||
// if it's actually in the document at this moment.
|
||||
if (!element.__attached && inDocument(element)) {
|
||||
// bail if the element is already marked as attached
|
||||
if (!element.__attached) {
|
||||
element.__attached = true;
|
||||
if (element.attachedCallback) {
|
||||
element.attachedCallback();
|
||||
@@ -144,9 +142,8 @@ function _detached(element) {
|
||||
// track element for removal if it's upgraded and cares about removal
|
||||
if (element.__upgraded__ &&
|
||||
(element.attachedCallback || element.detachedCallback)) {
|
||||
// bail if the element is already marked as not attached and proceed only
|
||||
// if it's actually *not* in the document at this moment.
|
||||
if (element.__attached && !inDocument(element)) {
|
||||
// bail if the element is already marked as not attached
|
||||
if (element.__attached) {
|
||||
element.__attached = false;
|
||||
if (element.detachedCallback) {
|
||||
element.detachedCallback();
|
||||
@@ -196,7 +193,7 @@ function watchShadow(node) {
|
||||
|
||||
node.appendChild(div).appendChild(child);
|
||||
*/
|
||||
function handler(mutations) {
|
||||
function handler(root, mutations) {
|
||||
// for logging only
|
||||
if (flags.dom) {
|
||||
var mx = mutations[0];
|
||||
@@ -213,13 +210,14 @@ function handler(mutations) {
|
||||
console.group('mutations (%d) [%s]', mutations.length, u || '');
|
||||
}
|
||||
// handle mutations
|
||||
var isAttached = inDocument(root);
|
||||
mutations.forEach(function(mx) {
|
||||
if (mx.type === 'childList') {
|
||||
forEach(mx.addedNodes, function(n) {
|
||||
if (!n.localName) {
|
||||
return;
|
||||
}
|
||||
addedNode(n);
|
||||
addedNode(n, isAttached);
|
||||
});
|
||||
forEach(mx.removedNodes, function(n) {
|
||||
if (!n.localName) {
|
||||
@@ -250,7 +248,7 @@ function takeRecords(node) {
|
||||
}
|
||||
var observer = node.__observer;
|
||||
if (observer) {
|
||||
handler(observer.takeRecords());
|
||||
handler(node, observer.takeRecords());
|
||||
takeMutations();
|
||||
}
|
||||
}
|
||||
@@ -265,7 +263,7 @@ function observe(inRoot) {
|
||||
}
|
||||
// For each ShadowRoot, we create a new MutationObserver, so the root can be
|
||||
// garbage collected once all references to the `inRoot` node are gone.
|
||||
var observer = new MutationObserver(handler);
|
||||
var observer = new MutationObserver(handler.bind(this, inRoot));
|
||||
observer.observe(inRoot, {childList: true, subtree: true});
|
||||
inRoot.__observer = observer;
|
||||
}
|
||||
@@ -274,7 +272,7 @@ function observe(inRoot) {
|
||||
function upgradeDocument(doc) {
|
||||
doc = window.wrap(doc);
|
||||
flags.dom && console.group('upgradeDocument: ', (doc.baseURI).split('/').pop());
|
||||
addedNode(doc);
|
||||
addedNode(doc, doc === window.wrap(document));
|
||||
observe(doc);
|
||||
flags.dom && console.groupEnd();
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ function upgradeWithDefinition(element, definition) {
|
||||
// attachedCallback fires in tree order, call before recursing
|
||||
scope.attachedNode(element);
|
||||
// there should never be a shadow root on element at this point
|
||||
scope.upgradeSubtree(element);
|
||||
scope.upgradeSubtree(element, element.__attached);
|
||||
flags.upgrade && console.groupEnd();
|
||||
// OUTPUT
|
||||
return element;
|
||||
|
||||
@@ -457,6 +457,47 @@ suite('customElements', function() {
|
||||
assert.deepEqual(['a', 'b', 'c', 'd', 'e'], log);
|
||||
});
|
||||
|
||||
test('attached and detached in same turn', function(done) {
|
||||
var log = [];
|
||||
var p = Object.create(HTMLElement.prototype);
|
||||
p.attachedCallback = function() {
|
||||
log.push('attached');
|
||||
};
|
||||
p.detachedCallback = function() {
|
||||
log.push('detached');
|
||||
};
|
||||
document.registerElement('x-ad', {prototype: p});
|
||||
var el = document.createElement('x-ad');
|
||||
work.appendChild(el);
|
||||
work.removeChild(el);
|
||||
setTimeout(function() {
|
||||
assert.deepEqual(['attached', 'detached'], log);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('detached and re-attached in same turn', function(done) {
|
||||
var log = [];
|
||||
var p = Object.create(HTMLElement.prototype);
|
||||
p.attachedCallback = function() {
|
||||
log.push('attached');
|
||||
};
|
||||
p.detachedCallback = function() {
|
||||
log.push('detached');
|
||||
};
|
||||
document.registerElement('x-da', {prototype: p});
|
||||
var el = document.createElement('x-da');
|
||||
work.appendChild(el);
|
||||
CustomElements.takeRecords();
|
||||
log = [];
|
||||
work.removeChild(el);
|
||||
work.appendChild(el);
|
||||
setTimeout(function() {
|
||||
assert.deepEqual(['detached', 'attached'], log);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('detachedCallback ordering', function() {
|
||||
var log = [];
|
||||
var p = Object.create(HTMLElement.prototype);
|
||||
|
||||
Reference in New Issue
Block a user