fix(email): render emails that put height:100% on a wrapper element
Some HTML emails set height:100% on a full-bleed wrapper table/div rather
than html/body. With the viewer's body{overflow:hidden}, this collapses
documentElement.scrollHeight to the iframe's 150px default, so the
scrollHeight-based auto-resize locks the iframe short and the body renders
blank below the fold. A Box.co.il verification email rendered as a
logo-only 150px strip.
- Neutralise height:100% on any element so the body grows to its content.
- Measure max(documentElement, body).scrollHeight, and re-measure on a
fixed cadence over a short settle window so a late reflow (height:100%
wrapper, or images that resize after onload) is caught even when no
ResizeObserver/image event fires.
This commit is contained in:
@@ -2117,6 +2117,12 @@ export function EmailViewer({
|
|||||||
fit - the latter wraps header text to one character per line, which reads
|
fit - the latter wraps header text to one character per line, which reads
|
||||||
as 90deg-rotated vertical headers (issue #409). */
|
as 90deg-rotated vertical headers (issue #409). */
|
||||||
html { overflow: hidden; height: auto !important; }
|
html { overflow: hidden; height: auto !important; }
|
||||||
|
/* Some emails put height:100% on a full-bleed wrapper table/div (not html/body),
|
||||||
|
which - with body's overflow:hidden - clips the content to a sliver, and the
|
||||||
|
scrollHeight-based auto-resize then locks the iframe short (a Box.co.il
|
||||||
|
verification email rendered as a logo-only 150px strip). Neutralise the
|
||||||
|
full-height trick on any element so the body grows to its content. */
|
||||||
|
[style*="height:100%"], [style*="height: 100%"] { height: auto !important; }
|
||||||
body { margin: 0; padding: ${bodyPadding}; overflow-x: auto; overflow-y: hidden; height: auto !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 14px; line-height: 1.6; color: #1a1a1a; background: #ffffff; word-wrap: break-word; overflow-wrap: break-word; }
|
body { margin: 0; padding: ${bodyPadding}; overflow-x: auto; overflow-y: hidden; height: auto !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 14px; line-height: 1.6; color: #1a1a1a; background: #ffffff; word-wrap: break-word; overflow-wrap: break-word; }
|
||||||
@media (max-width: 640px) { body { padding-left: ${mobileBodyPaddingX}; padding-right: ${mobileBodyPaddingX}; } }
|
@media (max-width: 640px) { body { padding-left: ${mobileBodyPaddingX}; padding-right: ${mobileBodyPaddingX}; } }
|
||||||
img { max-width: 100% !important; height: auto !important; }
|
img { max-width: 100% !important; height: auto !important; }
|
||||||
@@ -2164,15 +2170,30 @@ export function EmailViewer({
|
|||||||
const doc = iframe.contentDocument;
|
const doc = iframe.contentDocument;
|
||||||
if (doc?.body) {
|
if (doc?.body) {
|
||||||
// Auto-resize iframe to fit content
|
// Auto-resize iframe to fit content
|
||||||
const resizeObserver = new ResizeObserver(() => {
|
// Measure max(documentElement, body): a height:100% wrapper can leave
|
||||||
const height = doc.documentElement.scrollHeight;
|
// documentElement.scrollHeight short while the real content lives in body.
|
||||||
|
const applyHeight = () => {
|
||||||
|
if (iframe.contentDocument !== doc) return; // navigated away; stale
|
||||||
|
const height = Math.max(doc.documentElement.scrollHeight, doc.body.scrollHeight);
|
||||||
iframe.style.height = height + 'px';
|
iframe.style.height = height + 'px';
|
||||||
lastBodyHeightRef.current = height;
|
lastBodyHeightRef.current = height;
|
||||||
});
|
};
|
||||||
|
const resizeObserver = new ResizeObserver(applyHeight);
|
||||||
resizeObserver.observe(doc.body);
|
resizeObserver.observe(doc.body);
|
||||||
const initialHeight = doc.documentElement.scrollHeight;
|
applyHeight();
|
||||||
iframe.style.height = initialHeight + 'px';
|
// The ResizeObserver only fires on body's border box; a content overflow
|
||||||
lastBodyHeightRef.current = initialHeight;
|
// that grows scrollHeight without resizing that box (e.g. a height:100%
|
||||||
|
// wrapper, or images that reflow the layout after onload) is otherwise
|
||||||
|
// missed and the iframe stays short. Re-measure on a fixed cadence over a
|
||||||
|
// short settle window, then stop — a self-clearing catch-all that does
|
||||||
|
// not depend on image load/error events firing (blocked images may fire
|
||||||
|
// neither). Cheap: ~12 scrollHeight reads, no early-stop heuristic to
|
||||||
|
// mis-trigger on a brief-stable-then-grow reflow.
|
||||||
|
const poll = window.setInterval(() => {
|
||||||
|
if (iframe.contentDocument !== doc) { window.clearInterval(poll); return; }
|
||||||
|
applyHeight();
|
||||||
|
}, 200);
|
||||||
|
window.setTimeout(() => window.clearInterval(poll), 2400);
|
||||||
setIframeReady(true);
|
setIframeReady(true);
|
||||||
|
|
||||||
// Hide images that fail to load (dead/mixed-content/unreachable external
|
// Hide images that fail to load (dead/mixed-content/unreachable external
|
||||||
|
|||||||
Reference in New Issue
Block a user