Skip to content
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

Reprocess elements after specific changes are made to them #156

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

josefarias
Copy link
Contributor

@josefarias josefarias commented Dec 6, 2023

Coming from #155 by @northeastprince

Fixes #151

This PR creates mutation observers for every <time> element tracked by LocalTime so they'll be reprocessed when certain changes occur.

We do this through a new class called ElementObservations which keeps track of which elements already have observers, and also manages said observers.

Note that not removing an observer after an element is removed from the dom can cause memory leaks. So we've modified our PageObserver to notify the controller of any relevant <time> elements that are removed. Their observers are then promptly disconnected.

Also note that we modify the <time> elements as a response to their mutations. This is prone to infinite loops, so we must be careful. Thus, we're also adding Playwright to run integration tests so we can ship more confidently — and also assert that this will work with Turbo 8 morphs, which is the main motivation behind this change.

As a side-effect, the integration-tests page can be accessed manually as a sort of playground:

integration-tests page

Finally, if you wanted to test this out with actual Turbo 8 (instead of simulating a morph), you can do that by reverting 5ba6367. There's more info in the commit.

Comment on lines +11 to +14
function simulateMorph() {
const textNode = document.getElementById("one").firstChild
textNode.nodeValue = "changed"
}
Copy link
Contributor Author

@josefarias josefarias Dec 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +47 to +49
incrementUpdates: (element) =>
@elements.get(element).updates++
element.setAttribute("data-updates", @elements.get(element).updates)
Copy link
Contributor Author

@josefarias josefarias Dec 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only done for testing purposes.

We mutate the element as a response to mutations, so this PR is prone to infinite loops if we're not careful. I think asserting the number of updates is a good protection against this.

@josefarias josefarias marked this pull request as ready for review December 6, 2023 03:41
Comment on lines +34 to +37
element = if target.nodeType is Node.TEXT_NODE
target.parentNode
else
target
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turbo morphs could change either/or — the data attributes on the element itself, or the value of the nested text node


observe: (element) =>
observer = new MutationObserver(@processMutations)
observer.observe(element, characterData: true, subtree: true, attributes: true, attributeFilter: OBSERVABLE_ATTRIBUTES)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per Mozilla, setting textContent (which is what this library uses to update the element) removes all of the node's children and replaces them with a single text node with the given string value. So it shouldn't cause infinite loops due to this configuration.

This was also my experience while testing. But of course we'll want to thoroughly test on our apps before a public release.

@josefarias
Copy link
Contributor Author

@jorgemanrubia helpfully pointed out we might be able to simplify to a single observer + selector matching and an in-memory cache of the elements. I'll try that next.

@northeastprince
Copy link

@josefarias any update on this?

@josefarias
Copy link
Contributor Author

@josefarias any update on this?

Hey @northeastprince. No updates, sorry. It's looking like it might be a while before I personally can find time during work to tackle this.

Might tackle it recreationally in my time off but that's a big if as I've been rather busy.

In the meantime, you might try the event listener from your linked PR or mark the <time> elements as turbo-permanent. Sorry I don't have a holistic solution yet!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Existing elements change to their raw form when rerendered
2 participants