@@ -178,20 +178,31 @@ export async function captureDOM(element, options) {
178178 )
179179 if ( docH > 0 ) h0 = Math . max ( h0 , limitDecimals ( docH ) )
180180 if ( docW > 0 ) w0 = Math . max ( w0 , limitDecimals ( docW ) )
181- // Also measure clone in a temp container with injected styles (clone may layout differently)
181+ // Also measure clone in a temp container with injected styles (clone may layout differently).
182+ // PERF-3: cache result per element — this cloneNode(true) + layout round-trip is expensive
183+ // for large DOMs; reuse when the same element is captured with the same total CSS length.
182184 try {
183- const wrap = elDoc . createElement ( 'div' )
184- wrap . style . cssText = 'position:absolute!important;left:-9999px!important;top:0!important;width:' + w0 + 'px!important;overflow:visible!important;visibility:hidden!important;'
185- const styleNode = elDoc . createElement ( 'style' )
186- styleNode . textContent = ( state . scrollbarCSS || '' ) + state . baseCSS + state . fontsCSS + 'svg{overflow:visible;} foreignObject{overflow:visible;}' + state . classCSS
187- wrap . appendChild ( styleNode )
188- wrap . appendChild ( state . clone . cloneNode ( true ) )
189- elDoc . body . appendChild ( wrap )
190- const csh = wrap . scrollHeight
191- const csw = wrap . scrollWidth
192- elDoc . body . removeChild ( wrap )
193- if ( csh > 0 ) h0 = Math . max ( h0 , limitDecimals ( csh ) )
194- if ( csw > 0 ) w0 = Math . max ( w0 , limitDecimals ( csw ) )
185+ const cssLen = ( state . scrollbarCSS || '' ) . length + ( state . baseCSS || '' ) . length +
186+ ( state . fontsCSS || '' ) . length + ( state . classCSS || '' ) . length
187+ const hint = cache . measureHints . get ( state . element )
188+ if ( hint && hint . cssLen === cssLen && hint . w0 === w0 ) {
189+ if ( hint . csh > 0 ) h0 = Math . max ( h0 , limitDecimals ( hint . csh ) )
190+ if ( hint . csw > 0 ) w0 = Math . max ( w0 , limitDecimals ( hint . csw ) )
191+ } else {
192+ const wrap = elDoc . createElement ( 'div' )
193+ wrap . style . cssText = 'position:absolute!important;left:-9999px!important;top:0!important;width:' + w0 + 'px!important;overflow:visible!important;visibility:hidden!important;'
194+ const styleNode = elDoc . createElement ( 'style' )
195+ styleNode . textContent = ( state . scrollbarCSS || '' ) + state . baseCSS + state . fontsCSS + 'svg{overflow:visible;} foreignObject{overflow:visible;}' + state . classCSS
196+ wrap . appendChild ( styleNode )
197+ wrap . appendChild ( state . clone . cloneNode ( true ) )
198+ elDoc . body . appendChild ( wrap )
199+ const csh = wrap . scrollHeight
200+ const csw = wrap . scrollWidth
201+ elDoc . body . removeChild ( wrap )
202+ cache . measureHints . set ( state . element , { cssLen, w0, csh, csw } )
203+ if ( csh > 0 ) h0 = Math . max ( h0 , limitDecimals ( csh ) )
204+ if ( csw > 0 ) w0 = Math . max ( w0 , limitDecimals ( csw ) )
205+ }
195206 } catch { /* fallback: use doc dimensions above */ }
196207 }
197208 // === NEW: recompute height using the kept-children span (no offscreen) ===
0 commit comments