如何使用 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

然后你只需要包装你的应用程序,或者你想在&lt;WrappedJssComponenent&gt;&lt;YourComponent&gt;&lt;/YourComponent&gt;&lt;/WrappedJssComponenent&gt;中隐藏的应用程序部分。

小心,一些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”下的元素?

如何在 shadow dom 中使用全局 CSS 样式

如何使用gradle任务运行shadow jar?

如何在使用 cssSelector 清除 Chrome 浏览器的浏览数据时与 #shadow-root (open) 中的元素进行交互