问题
所以我有一个 Lit 元素,我正在尝试将其移动到新文档。我可以让它移动,并且所有功能仍然存在,以及它和原始文档之间的通信。然而它缺少所有的样式。
例如,如果我有一个简单的 Lit 元素:
@customElement("my-el")
export class customEl extends LitElement {
popout () {
const newWindow = window.open('', '_blank', 'title=Search Controls,height=1447,width=405,top=0,…o,status=no,menubar=no,scrollbars=no,resizable=no');
newWindow.document.body.appendChild( newWindow.document.adoptNode( this ) );
}
static styles = css`
.custom-el {
background-color: #666;
color: #fff;
}
.custom-el .custom-el_text {
padding: 15px;
border: 1px solid red;
font-family: "Comic Sans", "Arial", "Helvetica", sans-serif;
font-size: 44px;
font-weight: 900;
}
`;
render() {
return html`
<div class="custom-el">
<p class="custom-el_text">Hello World</p>
</div>
`;
}
}
如果popout
调用方法后,元素将移动到新窗口,除了样式外,所有内容都保持不变。
我尝试过的
深入研究 Lit NPM 包,我尝试追踪它设置 adoptedStyleSheets
的位置。属性(property)。我在window
上没有找到它, document
, renderRoot
,或shadowRoot
。我希望能够以某种方式将采用的样式表从一个文档迁移到另一个文档。
使用importNode
而不是adoptNode
。这会导致重新设置元素的状态,这对于我的用例来说是非常不希望的,但也会给出错误 Failed to set the 'adoptedStyleSheets' property on 'ShadowRoot': Sharing constructed stylesheets in multiple documents is not allowed
。我认为这可能是样式没有移动的根源,但我不知道该怎么办。
我需要什么
需要一种方法将元素从一个文档移动到另一个文档,同时保留状态、功能和样式。当前方法涵盖了状态和功能,但不保留样式。寻找一种在新窗口(弹出窗口)中打开元素时维护所有这三个元素的方法。
工作示例
您可以在这里找到此问题的有效示例:https://codepen.io/Arrbjorn/pen/bGaPMBa
最佳答案
问题的根源
因此,这里的问题是构造样式表的规范不支持跨文档共享构造样式表,因此错误Failed to set the 'adoptedStyleSheets' property on 'ShadowRoot':Sharingbuilt stylesheets in multiple Documents 不是允许
。
有关为何做出此决定的更多信息,您可以在此处阅读原始讨论:https://github.com/WICG/construct-stylesheets/issues/23
解决方案
但是,对于需要在新文档中打开的 Web 组件,无论是 iframe
、新 窗口
还是其他任何内容,都有一种解决方法。我们可以在 Web 组件的 adoptedCallback
函数中解决这个所需的行为。下面是一个示例:
import { CSSResultGroup, supportsAdoptingStyleSheets } from "lit";
adoptedCallback(){
// If the browser supports adopting stylesheets
if (supportsAdoptingStyleSheets) {
// If the styles is an array of CSSResultGroup Objects
// This happens when styles is passed an array i.e. => static styles = [css`${styles1}`, css`${styles2}`] in the component
if ( ((this.constructor as typeof LitElement).styles as CSSResultGroup[]).length ) {
// Define the sheets array by mapping the array of CSSResultGroup objects
const sheets = ((this.constructor as typeof LitElement).styles as CSSResultGroup[]).map( s => {
// Create a new stylesheet in the context of the owner document's window
// We have to cast defaultView as any due to typescript definition not allowing us to call CSSStyleSheet in this conext
// We have to cast CSSStyleSheet as <any> due to typescript definition not containing replaceSync for CSSStyleSheet
const sheet = (new (this.ownerDocument.defaultView as any).CSSStyleSheet() as any);
// Update the new sheet with the old styles
sheet.replaceSync(s);
// Return the sheet
return sheet;
});
// Set adoptedStyleSheets with the new styles (must be an array)
(this.shadowRoot as any).adoptedStyleSheets = sheets;
} else {
// Create a new stylesheet in the context of the owner document's window
// We have to cast defaultView as any due to typescript definition not allowing us to call CSSStyleSheet in this conext
// We have to cast CSSStyleSheet as <any> due to typescript definition not containing replaceSync for CSSStyleSheet
const sheet = (new (this.ownerDocument.defaultView as any).CSSStyleSheet() as any);
// Update the new sheet with the old styles
sheet.replaceSync( (this.constructor as typeof LitElement).styles );
// Set adoptedStyleSheets with the new styles (must be an array)
(this.shadowRoot as any).adoptedStyleSheets = [ sheet ];
}
}
}
这是在做什么?
此代码块引用自定义元素的原始样式,并在新文档的上下文中创建新的 CSSStyleSheet
(或 CssStyleSheet[]
)并应用它们到元素。这样,我们就不会将原始样式共享到新文档,而是使用新文档创建的新样式表。由创建。
优化
如果您像我一样在许多可能被移动到另一个文档的组件中使用它,您可以将其抽象为一个实用函数,该函数可以导入到任何组件中并在 adoptedCallback
期间调用。
函数
import { CSSResultGroup, supportsAdoptingStyleSheets } from "lit";
// This function migrates styles from a custom element's constructe stylesheet to a new document.
export function adoptStyles ( shadowRoot: ShadowRoot, styles: CSSResultGroup | CSSResultGroup[], defaultView: Window) {
// If the browser supports adopting stylesheets
if (supportsAdoptingStyleSheets) {
// If the styles is an array of CSSResultGroup Objects
// This happens when styles is passed an array i.e. => static styles = [css`${styles1}`, css`${styles2}`] in the component
if ( (styles as CSSResultGroup[]).length ) {
// Define the sheets array by mapping the array of CSSResultGroup objects
const sheets = (styles as CSSResultGroup[]).map( s => {
// Create a new stylesheet in the context of the owner document's window
// We have to cast defaultView as any due to typescript definition not allowing us to call CSSStyleSheet in this conext
// We have to cast CSSStyleSheet as <any> due to typescript definition not containing replaceSync for CSSStyleSheet
const sheet = (new (defaultView as any).CSSStyleSheet() as any);
// Update the new sheet with the old styles
sheet.replaceSync(s);
// Return the sheet
return sheet;
});
// Set adoptedStyleSheets with the new styles (must be an array)
(shadowRoot as any).adoptedStyleSheets = sheets;
} else {
// Create a new stylesheet in the context of the owner document's window
// We have to cast defaultView as any due to typescript definition not allowing us to call CSSStyleSheet in this conext
// We have to cast CSSStyleSheet as <any> due to typescript definition not containing replaceSync for CSSStyleSheet
const sheet = (new (defaultView as any).CSSStyleSheet() as any);
// Update the new sheet with the old styles
sheet.replaceSync(styles);
// Set adoptedStyleSheets with the new styles (must be an array)
(shadowRoot as any).adoptedStyleSheets = [ sheet ];
}
}
}
从组件调用函数
adoptedCallback() {
// Adopt the old styles into the new document
adoptStyles( this.shadowRoot!, (this.constructor as typeof LitElement).styles!, this.ownerDocument.defaultView! )
}
关于javascript - 将 Lit 元素移动到新文档时未保留样式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72016726/