Skip to content

Commit

Permalink
Issue #34: Ensure that MutationObserver tracks changes to attributes …
Browse files Browse the repository at this point in the history
…as expected even if element is disconnected from DOM and reconnected again later.
  • Loading branch information
patricknelson committed Dec 6, 2023
1 parent abce694 commit 70dd146
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 1 deletion.
1 change: 0 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const propMapCache = new Map();
// NOTE: We can .observe() many separate elements and not have to .disconnect() each one individually, since if the
// element being observed is removed from the DOM and released by the garbage collector, the MutationObserver will
// stop observing the removed element automatically.
// TODO: Validate that disconnected/reconnected elements are still being observed properly (e.g. if drag/dropped in the DOM via DevTools).
const attributeObserver = new MutationObserver((mutations) => {
// Go through each mutation and forward the updated attribute value to the corresponding Svelte prop.
mutations.forEach(mutation => {
Expand Down
28 changes: 28 additions & 0 deletions tests/AttributeChanges.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,34 @@ describe('Forwarding of attribute changes', () => {
await tick(); // required for svelte to apply update
expect(el.querySelector('.foo').innerHTML).toBe('changed 1');
expect(el.querySelector('.bar').innerHTML).toBe('changed 2');

/**
* Below we are ensuring that MutationObserver is still following changes to attributes even if the element is
* removed or relocated.
*/

// Remove customEl from parent and append to a new parent
customEl.remove();
el.innerHTML = '<div></div>';

// ... ensure that the parent is empty and that the element is fully disconnected from the DOM (but not garbage collected)
expect(customEl.parentElement).toBe(null);
expect(customEl.isConnected).toBe(false);
expect(el.querySelector('.foo')).toBe(null);
expect(el.querySelector('.bar')).toBe(null);

// ... prove that it was relocated in DOM by running last test again but ensuring original values.
el.appendChild(customEl);
expect(el.querySelector('.foo').innerHTML).toBe('changed 1');
expect(el.querySelector('.bar').innerHTML).toBe('changed 2');

// Change values again and verify once more than changes are reflected
customEl.setAttribute('foo', 'changed 3');
customEl.setAttribute('bar', 'changed 4');
await tick(); // required for svelte to apply update
expect(el.querySelector('.foo').innerHTML).toBe('changed 3');
expect(el.querySelector('.bar').innerHTML).toBe('changed 4');

});

test('boolean "attributes": forward all attributes if set to true (from default state)', async () => {
Expand Down

0 comments on commit 70dd146

Please sign in to comment.