diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index 287c16b5fc5..ca17072bd6c 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -154,14 +154,9 @@ export function get_root_for_style(node: Node): ShadowRoot | Document { return node.ownerDocument; } -export function append_empty_stylesheet(node: Node) { - const style_element = element('style') as HTMLStyleElement; - append_stylesheet(get_root_for_style(node), style_element); - return style_element.sheet as CSSStyleSheet; -} - -function append_stylesheet(node: ShadowRoot | Document, style: HTMLStyleElement) { +export function append_stylesheet(node: ShadowRoot | Document, style: HTMLStyleElement) { append((node as Document).head || node, style); + return style.sheet as CSSStyleSheet; } export function append_hydration(target: NodeEx, node: NodeEx) { diff --git a/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index 6907e5af029..cf9a224a30c 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -1,8 +1,8 @@ -import { append_empty_stylesheet, get_root_for_style } from './dom'; +import { append_stylesheet, detach, element, get_root_for_style } from './dom'; import { raf } from './environment'; interface StyleInformation { - stylesheet: CSSStyleSheet; + style_element: HTMLStyleElement; rules: Record; } @@ -20,8 +20,8 @@ function hash(str: string) { return hash >>> 0; } -function create_style_information(doc: Document | ShadowRoot, node: Element & ElementCSSInlineStyle) { - const info = { stylesheet: append_empty_stylesheet(node), rules: {} }; +function create_style_information(doc: Document | ShadowRoot) { + const info = { style_element: element('style'), rules: {} }; managed_styles.set(doc, info); return info; } @@ -39,9 +39,10 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: const name = `__svelte_${hash(rule)}_${uid}`; const doc = get_root_for_style(node); - const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node); - + const { style_element, rules } = managed_styles.get(doc) || create_style_information(doc); + if (!rules[name]) { + const stylesheet = append_stylesheet(doc, style_element); rules[name] = true; stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); } @@ -50,6 +51,7 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: node.style.animation = `${animation ? `${animation}, ` : ''}${name} ${duration}ms linear ${delay}ms 1 both`; active += 1; + return name; } @@ -71,9 +73,8 @@ export function clear_rules() { raf(() => { if (active) return; managed_styles.forEach(info => { - const { stylesheet } = info; - let i = stylesheet.cssRules.length; - while (i--) stylesheet.deleteRule(i); + const { style_element } = info; + detach(style_element); info.rules = {}; }); managed_styles.clear(); diff --git a/test/runtime/samples/style_manager-cleanup/_config.js b/test/runtime/samples/style_manager-cleanup/_config.js new file mode 100644 index 00000000000..2367e81914f --- /dev/null +++ b/test/runtime/samples/style_manager-cleanup/_config.js @@ -0,0 +1,18 @@ +export default { + skip_if_ssr: true, + skip_if_hydrate: true, + skip_if_hydrate_from_ssr: true, + + async test({ raf, assert, component, window }) { + component.visible = true; + raf.tick(100); + component.visible = false; + raf.tick(200); + raf.tick(0); + + assert.htmlEqual( + window.document.head.innerHTML, + '' + ); + } +}; diff --git a/test/runtime/samples/style_manager-cleanup/main.svelte b/test/runtime/samples/style_manager-cleanup/main.svelte new file mode 100644 index 00000000000..7d5595d5aa0 --- /dev/null +++ b/test/runtime/samples/style_manager-cleanup/main.svelte @@ -0,0 +1,14 @@ + + +{#if visible} +
+{/if} \ No newline at end of file