Skip to content

Commit b9f5ecb

Browse files
Refactor style element cloning for shadow DOM integration
- Introduced helper functions to clone style and link elements, improving code readability and maintainability. - Added functionality to extract CSS rules from style elements using the CSSOM when textContent is unavailable. - Enhanced the cloning process to ensure accurate representation of styles in the shadow DOM.
1 parent 77da5e3 commit b9f5ecb

1 file changed

Lines changed: 91 additions & 14 deletions

File tree

cms-slider/src/hooks/useShadowGlobalStyles.tsx

Lines changed: 91 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,97 @@ function copyGlobalStylesToShadow(shadowRoot: ShadowRoot) {
3838
);
3939

4040
styleElements.forEach((el) => {
41-
let clone: HTMLElement | null = null;
42-
43-
// Clone inline styles
44-
if (el.tagName === "STYLE") {
45-
clone = document.createElement("style");
46-
(clone as HTMLStyleElement).textContent = el.textContent;
47-
}
48-
// Clone linked stylesheets
49-
else if (el.tagName === "LINK" && (el as HTMLLinkElement).href) {
50-
clone = document.createElement("link");
51-
(clone as HTMLLinkElement).rel = "stylesheet";
52-
(clone as HTMLLinkElement).href = (el as HTMLLinkElement).href;
41+
const clone = cloneStyleElement(el);
42+
if (clone) {
43+
shadowRoot.appendChild(clone);
5344
}
54-
55-
if (clone) shadowRoot.appendChild(clone);
5645
});
5746
}
47+
48+
/**
49+
* Clones a style or link element for injection into shadow DOM.
50+
*
51+
* @param el - The style or link element to clone
52+
* @returns A cloned HTMLElement ready for shadow DOM, or null if element cannot be cloned
53+
*/
54+
function cloneStyleElement(el: Element): HTMLElement | null {
55+
// Clone inline <style> elements
56+
if (el.tagName === "STYLE") {
57+
return cloneInlineStyleElement(el as HTMLStyleElement);
58+
}
59+
60+
// Clone external <link> stylesheets
61+
if (el.tagName === "LINK" && (el as HTMLLinkElement).href) {
62+
return cloneLinkElement(el as HTMLLinkElement);
63+
}
64+
65+
return null;
66+
}
67+
68+
/**
69+
* Clones an inline style element, preserving its CSS content.
70+
* Falls back to CSSOM extraction if textContent is empty.
71+
*
72+
* @param el - The style element to clone
73+
* @returns A new style element with the same CSS content
74+
*/
75+
function cloneInlineStyleElement(el: HTMLStyleElement): HTMLStyleElement {
76+
const clone = document.createElement("style");
77+
const textContent = el.textContent?.trim();
78+
79+
// Use textContent if available, otherwise extract from CSSOM
80+
if (textContent) {
81+
clone.textContent = textContent;
82+
} else {
83+
clone.textContent = getStyleElementCSS(el);
84+
}
85+
86+
return clone;
87+
}
88+
89+
/**
90+
* Clones a link element for external stylesheets.
91+
*
92+
* @param el - The link element to clone
93+
* @returns A new link element pointing to the same stylesheet
94+
*/
95+
function cloneLinkElement(el: HTMLLinkElement): HTMLLinkElement {
96+
const clone = document.createElement("link");
97+
clone.rel = "stylesheet";
98+
clone.href = el.href;
99+
return clone;
100+
}
101+
102+
/**
103+
* Extracts CSS rules from a style element by accessing its associated stylesheet.
104+
*
105+
* This function is necessary because sometimes `element.textContent` is empty or unreliable
106+
* for dynamically created style elements, but the actual CSS rules are accessible via the
107+
* CSSOM (CSS Object Model) through `document.styleSheets`.
108+
*
109+
* @param el - The HTML element to extract CSS from (should be a style element)
110+
* @returns The concatenated CSS text from all rules in the stylesheet, or empty string if the element is not a style element or if the stylesheet is not found.
111+
*/
112+
function getStyleElementCSS(el: HTMLElement) {
113+
if (!(el instanceof HTMLStyleElement)) {
114+
return "";
115+
}
116+
117+
// Find the CSSStyleSheet object associated with this style element
118+
const sheet = Array.from(document.styleSheets).find(
119+
(s) => s.ownerNode === el
120+
);
121+
122+
if (!sheet) return "";
123+
124+
try {
125+
// Extract and concatenate all CSS rules from the stylesheet
126+
return Array.from(sheet.cssRules)
127+
.map((rule) => rule.cssText)
128+
.join("\n");
129+
} catch (e) {
130+
// CORS restrictions prevent reading cross-origin stylesheets
131+
console.warn("Unable to read CSS rules (maybe cross-origin):", e);
132+
return "";
133+
}
134+
}

0 commit comments

Comments
 (0)