Running CodeMirror in ShadowDOM ShadowRoot
causes the styles not to be applied.
It is tricky to get the styles to apply because they are injected by CodeMirror’s code into the head of the document, from within the CM’s JavaScript code. The package does not include standalone .css
files:
Shipping standalone files would make it easy to include the style in a ShadowRoot
.
Here is a partial and incomplete way of finding and inserting the dynamic style:
let cmStyle
const findCMStyle = () => {
const style = Array.from(document.head.querySelectorAll('style')).find(n => n.textContent?.includes('.ͼ1'))
if (style) {
cmStyle = style.cloneNode(true)
// ... APPEND STYLE TO SHADOW ROOT ...
} else requestAnimationFrame(findCMStyle)
}
findCMStyle()
However, when this code eventually finds the <style>
element, the textContent
only has part of the full style, so the style only partially works. For example, at this point, in my case, it is always missing the cm-lintRange
and cm-lintRange-error
style definitions, so red squiggly lines will not appear in the text.
It appears that the JS code that injects the style, does so piecemeal in some way. Let’s try to observe it.
If at the moment that the <style>
is found we add a MutationObserver
to try and catch the rest of the textContent
by observing mutations, it does not work:
let cmStyle
const findCMStyle = () => {
const style = Array.from(document.head.querySelectorAll('style')).find(n => n.textContent?.includes('.ͼ1'))
if (style) {
cmStyle = style.cloneNode(true)
// ... APPEND STYLE TO SHADOW ROOT ...
const observer = new MutationObserver(() => {
console.log('STYLE CHANGED') // THIS NEVER HAPPENS
cmStyle = style.cloneNode(true)
// ... REPLACE STYLE IN SHADOW ROOT ...
})
observer.observe(style, {characterData: true, subtree: true})
// ... DON'T FORGET TO CLEAN UP THE OBSERVER ...
} else requestAnimationFrame(findCMStyle)
}
findCMStyle()
I’m not sure why the MutationObserver
never fires. Maybe because the JS code is injecting styles via CSSOM and there’s an edge case MutationObserver
doesn’t handle? If so, we should report this to Chrome issue tracker.
To work around MutationObserver
not being able to detect the textContent
change (note that one can see the textContent
in the element inspector), using a setTimeout
finally does the trick,
let cmStyle
const findCMStyle = () => {
const style = Array.from(document.head.querySelectorAll('style')).find(n => n.textContent?.includes('.ͼ1'))
if (style) {
cmStyle = style.cloneNode(true)
// ... APPEND STYLE TO SHADOW ROOT ...
setTimeout(() => {
cmStyle = style.cloneNode(true)
// ... REPLACE STYLE IN SHADOW ROOT ...
}, 1000)
} else requestAnimationFrame(findCMStyle)
}
findCMStyle()
but this feels a little more hacky: suppose there’s a race condition for some reason, hopefully 1 whole second is always enough though. It is currently working for me, assuming 1 second is always good.
Can you please ship the .css
within the package?
One way to do this is to simply ship the src/
folder with each package, then we can handle transpiling source code any way we want.
Another way to do it is to make a CSS-specific build to output just .css
.