fix(image): reconcile mj-image DOM on render to avoid <img> reload/flicker#432
Open
geonsang-jo wants to merge 1 commit into
Open
fix(image): reconcile mj-image DOM on render to avoid <img> reload/flicker#432geonsang-jo wants to merge 1 commit into
geonsang-jo wants to merge 1 commit into
Conversation
…icker The base MJML view re-renders a component by replacing its entire subtree (`this.el.innerHTML = getTemplateFromMjml()`) on every change:attributes / change:src. For mj-image this destroys and recreates the <img> node, so the browser drops the decoded image and refetches/repaints it — a visible flicker even when src did not change. Override the mj-image view's render to reconcile the freshly compiled subtree into the existing DOM instead: matching nodes are kept (preserving the <img> identity) and only changed attributes are written in place. The override mirrors coreMjmlView.render exactly except for that one substitution. Adding/removing a link wrapper (td > img <-> td > a > img) changes the structure, so the index-based reconciliation intentionally replaces the <img> on that transition; this is documented and locked by a test. Add tests/specs/image-dom-identity.test.ts covering node-identity preservation across attribute/src/style edits, link-wrapper add/remove, and the no-refetch invariant (a non-src edit must not re-set src).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #431
Problem
Editing any attribute/style of an
mj-imagemakes the rendered<img>flicker: the browser refetches and repaints the image on every edit.Side-by-side repro on
grapesjs-mjml@1.0.8— toggle the width and watch the Baseline image reload while the Patched one stays put.Root cause
The base MJML view re-renders by replacing the whole subtree:
this.el.innerHTML = this.getTemplateFromMjml()runs on everychange:attributes/change:src. That destroys and recreates the<img>node each time, so the browser drops the decoded image and fetches/repaints it.Fix
An
mj-image-onlyrenderoverride that reconciles the freshly compiled subtree into the existing DOM instead of replacinginnerHTML: matching nodes are kept (preserving the<img>identity) and only changed attributes are written in place. It mirrorscoreMjmlView.renderexactly except for that one substitution, and follows the existing per-component override pattern (Column,NavBar).srcedits (alt/title/style/padding) never touch the<img>'ssrc, so no refetch/flicker.srcedit updates the same node'ssrcin place.Scope & known limitation
Scoped to
mj-image. Adding/removing a link wrapper changes the structure (td > img↔td > a > img), so the index-based reconciliation replaces the<img>on that transition rather than moving it — documented in code and locked by a test so the boundary is intentional.Tests
New
tests/specs/image-dom-identity.test.tscovering node-identity preservation across attribute/src/style edits, link-wrapper add/remove, and the core no-refetch invariant (a non-srcedit must not re-setsrc). All existing tests pass;eslintandtsc --noEmitare clean. No public API change.