如何使用 cssinjs/jss 在 shadow root 中挂载样式
Posted
技术标签:
【中文标题】如何使用 cssinjs/jss 在 shadow root 中挂载样式【英文标题】:How to mount styles inside shadow root using cssinjs/jss 【发布时间】:2019-06-23 02:46:55 【问题描述】:我正在尝试在 shadow dom 中使用 https://material-ui.com/ 组件,并且需要一种将这些样式注入到 shadow dom 中的方法。默认情况下material-ui
,它在后台使用jss
在页面头部注入样式。
这可能吗?谁能举个例子?
【问题讨论】:
我不认为将 React 和整个 shebang 注入页面是一个好主意,在我看来当然不是这样。通常的方法是在你的扩展(在 web_accessible_resources 中列出)中只插入一个指向 html 文件的 iframe,这是一个标准页面,可以做任何想做的事情并在其中加载任何材料 UI 组件。我在网上至少看到过一个完整的示例/教程。 你想要一个组件的样式到它的影子 DOM 中还是你可以把所有的样式放到一个影子 DOM 中? @wOxxOm 我完全同意,但是 iframe 方法在我的经验中有点棘手,因为焦点(当您在 iframe 内单击时就像在另一个页面中一样)和调整 iframe 的大小(例如,当我在 iframe 中放置一个下拉列表时,我遇到了问题,我需要告诉外部页面调整 iframe 的大小,以便内容可见)。 Web 组件肯定会更加原生。 “epsilon”我想做的是使用很少的 material-ui 小部件制作一个 Web 组件,并且它们的样式应该注入到 shadow root 中。 即使您能够归档您的目标,您也可能会遇到从影子根内部显示弹出材质组件的问题。因为似乎材料库将弹出窗口 dom 元素直接插入到 html 页面正文中,在您的情况下这将是无样式的。 【参考方案1】:这就是我的 web 组件的样子,它是一个 web 组件,它呈现一个包含 material-ui 样式的 React 应用程序。
import * as React from 'react';
import render from 'react-dom';
import StylesProvider, jssPreset from '@material-ui/styles';
import create from 'jss';
import App from '@myApp/core';
class MyWebComponent extends HTMLElement
connectedCallback()
const shadowRoot = this.attachShadow( mode: 'open' );
const mountPoint = document.createElement('span');
const reactRoot = shadowRoot.appendChild(mountPoint);
const jss = create(
...jssPreset(),
insertionPoint: reactRoot
);
render(
<StylesProvider jss=jss>
<App />
</StylesProvider>,
mountPoint
);
customElements.define('my-web-commponent', MyWebComponent);
将 jss 上的 insertionPoint
设置为 shadow root 中的实际 react root 将告诉 jss 将这些样式插入到该 shadow root 中。
【讨论】:
这适用于许多组件,但是如果你使用 Dialog,它的标签被放置在 body 中,这个技巧不起作用。【参考方案2】:使用https://github.com/Wildhoney/ReactShadow 来创建shadow dom(你也可以手动完成,如上一个答案所示),我创建了一个封装逻辑的小型 WrapperComponent。
import root from 'react-shadow';
import jssPreset, StylesProvider from "@material-ui/styles";
import create from 'jss';
import React, useState from "react"
const WrappedJssComponent = (children) =>
const [jss, setJss] = useState(null);
function setRefAndCreateJss(headRef)
if (headRef && !jss)
const createdJssWithRef = create(...jssPreset(), insertionPoint: headRef)
setJss(createdJssWithRef)
return (
<root.div>
<head>
<style ref=setRefAndCreateJss></style>
</head>
jss &&
<StylesProvider jss=jss>
children
</StylesProvider>
</root.div>
)
export default WrappedJssComponent
然后你只需要包装你的应用程序,或者你想在<WrappedJssComponenent><YourComponent></YourComponent></WrappedJssComponenent>
中隐藏的应用程序部分。
小心,一些material-UI组件不能像往常一样工作(我遇到了一些麻烦
ClickAwayListener,可能是因为它使用了父 dom,说实话并没有调查更多。 Popper,以及所有尝试使用 document.body 作为容器的东西都无法访问影子节点中定义的 jss。您应该在 shadow dom 中提供一个元素作为容器。【讨论】:
这太棒了!你完全救了我!谢谢! "你应该在 shadow dom 中提供一个元素作为容器。" - 如何做到这一点? @Valu3 您可以参考容器元素将容器道具传递给 Popper(和其他一些元素) - 该元素也可以在阴影根内。以上是关于如何使用 cssinjs/jss 在 shadow root 中挂载样式的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Python 在 selenium 中编辑#shadow-root(用户代理)值
如何在 jQuery animate() 中使用 box-shadow?
如何使用 jquery 和 selenium 在“chrome://downloads”访问“shadow-root”下的元素?
如何在使用 cssSelector 清除 Chrome 浏览器的浏览数据时与 #shadow-root (open) 中的元素进行交互