@@ -194,23 +194,45 @@ function getAbsoluteSrcsetString(doc: Document, attributeValue: string) {
194
194
return output . join ( ', ' ) ;
195
195
}
196
196
197
+ const cachedDocument = new WeakMap < Document , HTMLAnchorElement > ( ) ;
198
+
197
199
export function absoluteToDoc ( doc : Document , attributeValue : string ) : string {
198
200
if ( ! attributeValue || attributeValue . trim ( ) === '' ) {
199
201
return attributeValue ;
200
202
}
201
- const a : HTMLAnchorElement = doc . createElement ( 'a' ) ;
202
- a . href = attributeValue ;
203
- return a . href ;
203
+
204
+ return getHref ( doc , attributeValue ) ;
204
205
}
205
206
206
207
function isSVGElement ( el : Element ) : boolean {
207
208
return Boolean ( el . tagName === 'svg' || ( el as SVGElement ) . ownerSVGElement ) ;
208
209
}
209
210
210
- function getHref ( ) {
211
- // return a href without hash
212
- const a = document . createElement ( 'a' ) ;
211
+ function getHref ( doc : Document , customHref ?: string ) {
212
+ let a = cachedDocument . get ( doc ) ;
213
+
214
+ if ( ! a ) {
215
+ a = doc . createElement ( 'a' ) ;
216
+ cachedDocument . set ( doc , a ) ;
217
+ }
218
+
219
+ /**
220
+ * This is needed for SPAs.
221
+ *
222
+ * Basically, the cached a link stores the URL when the element is created.
223
+ * SPAs will not update the page but it can change the URL, this makes the cached a link unusable.
224
+ *
225
+ * In order to prevent that, I need to reset the cache by defining the URL to any value,
226
+ * then I need to reset again to set the URL to '' to support relative paths like ./image.png
227
+ * to be correctly displayed.
228
+ *
229
+ * URLs are not updated if you set the same value, that's why I need to reset the value twice.
230
+ */
231
+ a . href = './clear-current' ;
213
232
a . href = '' ;
233
+
234
+ if ( customHref ) a . href = customHref ;
235
+
214
236
return a . href ;
215
237
}
216
238
@@ -242,7 +264,7 @@ export function transformAttribute(
242
264
} else if ( name === 'srcset' ) {
243
265
return getAbsoluteSrcsetString ( doc , value ) ;
244
266
} else if ( name === 'style' ) {
245
- return absoluteToStylesheet ( value , getHref ( ) ) ;
267
+ return absoluteToStylesheet ( value , getHref ( doc ) ) ;
246
268
} else if ( tagName === 'object' && name === 'data' ) {
247
269
return absoluteToDoc ( doc , value ) ;
248
270
}
@@ -565,7 +587,7 @@ function serializeTextNode(
565
587
n ,
566
588
) ;
567
589
}
568
- textContent = absoluteToStylesheet ( textContent , getHref ( ) ) ;
590
+ textContent = absoluteToStylesheet ( textContent , getHref ( document ) ) ;
569
591
}
570
592
if ( isScript ) {
571
593
textContent = 'SCRIPT_PLACEHOLDER' ;
@@ -659,7 +681,7 @@ function serializeElementNode(
659
681
( n as HTMLStyleElement ) . sheet as CSSStyleSheet ,
660
682
) ;
661
683
if ( cssText ) {
662
- attributes . _cssText = absoluteToStylesheet ( cssText , getHref ( ) ) ;
684
+ attributes . _cssText = absoluteToStylesheet ( cssText , getHref ( doc ) ) ;
663
685
}
664
686
}
665
687
// form fields
0 commit comments