首页 文章

使用链接样式阻止阴影DOM渲染

提问于
浏览
3

Shadow DOM支持使用 <link> 标签加载样式,这些样式的作用方式与使用 <style> 声明的样式相同,这非常方便,但是只有在样式准备好并且样式在那里加载时才会出现样式的问题是自定义元素的 :defined 伪选择器无法阻止的FOUC . 我遇到的另一个问题是在构造或连接自定义元素时测量阴影根中的元素,因为在加载和应用样式表之后已知"real"维度,我不知道什么时候会发生(可能是 ResizeObserver 将实施时的帮助?)
任何人都可以想出一个聪明的方法来解决这些问题(没有手动或使用构建步骤内联样式)?我的顾虑是否有意义?在 <head> 块渲染中 <link rel="stylesheet> 可以看作是一个错误吗?这个特征应该类似于那个?

2 回答

  • 0

    如果要避免使用FOUC,则应该隐藏元素,直到应用样式为止 . 使用 fetchXMLHttpRequest<link onload=...> 时,您可以知道何时加载样式 .

    关于维度问题,它不是特定于Shadow DOM,而是CSS体系结构的结果 .

    无论如何,通常建议设置元素的宽度和高度以避免FOUC,但也要避免整页重绘,以便加快渲染速度 .

    请注意,Shadow DOM中的 <link rel="stylsheet"> 支持是非常新的,因此它可能无法在所有浏览器中按预期工作 .

  • 1

    以下是使用shadowDOM创建组件的一些代码 . 为简单起见,我使用 setTimeout 模拟CSS的长加载时间 . 1秒钟后,我将CSS应用到元素中 .

    如你所述,元素看起来只有一种方式,直到加载CSS .

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>FOUC Prevention for WC</title>
        <script>
        var delayedStyles = document.createElement('style');
        delayedStyles.textContent = `
        :host {
          background-color: #DEE;
          border: 1px solid #999;
          display: block;
          width: 400px;
        }
    
        h1 {
          color: green;
          font: 18px/1em Tahoma;
          padding: 3px 6px;
        }
    
        p {
          margin: 5px 10px;
          padding: 10px;
        }
        `;
        var template = document.createElement('div');
        template.innerHTML = `
        <h1>The header</h1>
        <p>Some body content</p>
        `;
    
        // Class for `<my-component>`
        class MyComponent extends HTMLElement {
          constructor() {
            super();
    
            var sr = this.attachShadow({mode: 'open'});
            setTimeout(() => sr.appendChild(delayedStyles.cloneNode(true)), 1000);
    
    
            sr.appendChild(template.cloneNode(true));
          }
        }
    
        // Define our web component
        customElements.define('my-component', MyComponent);
        </script>
      </head>
      <body>
        <my-component></my-component>
      </body>
    </html>
    

    通过微小的更改,我们可以隐藏元素,直到CSS加载:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>FOUC Prevention for WC</title>
        <script>
        var delayedStyles = document.createElement('style');
        delayedStyles.textContent = `
        :host {
          background-color: #DEE;
          border: 1px solid #999;
          display: block;
          width: 400px;
        }
    
        .innerShell {
          display: block !important;
        }
    
        h1 {
          color: green;
          font: 18px/1em Tahoma;
          padding: 3px 6px;
        }
    
        p {
          margin: 5px 10px;
          padding: 10px;
        }
        `;
        var template = document.createElement('div');
        template.setAttribute('style', 'display: none;');
        template.className = 'innerShell';
        template.innerHTML = `
        <h1>The header</h1>
        <p>Some body content</p>
        `;
    
        // Class for `<my-component>`
        class MyComponent extends HTMLElement {
          constructor() {
            super();
    
            var sr = this.attachShadow({mode: 'open'});
            setTimeout(() => sr.appendChild(delayedStyles.cloneNode(true)), 1000);
    
    
            sr.appendChild(template.cloneNode(true));
          }
        }
    
        // Define our web component
        customElements.define('my-component', MyComponent);
        </script>
      </head>
      <body>
        <my-component></my-component>
      </body>
    </html>
    

    为此,我在我的shadow DOM中设置了我的顶级元素的 style 标记 . 我把它设置为 display: none; 这隐藏了影子DOM的内部内容 .

    然后,1秒后,当加载CSS时,它会使用 display: block !important 覆盖 display: none; . 我必须使用 !important 变得比 style 标签中设置的css更具体 .

    只有在CSS加载后,我的元素才会变得可见 .

    作为另一个选项您还可以在 <link> 标记上放置 onload 事件处理程序:

    <link rel="stylesheet" href="mystylesheet.css" onload="sheetLoaded()" onerror="sheetError()">

    在代码中,它会让您知道它已加载,然后只删除 style 属性 .

相关问题