【问题标题】:Block shadow DOM rendering with linked styles使用链接样式阻止阴影 DOM 渲染
【发布时间】:2018-04-16 11:01:39
【问题描述】:

Shadow DOM 支持使用<link> 标签来加载样式,其范围与使用<style> 声明的样式相同,这非常方便,但有一个问题是样式只有在它们准备好时才会应用例如,在加载样式时,使用自定义元素的 :defined 伪选择器无法阻止 FOUC。我遇到的另一个问题是在构建或连接自定义元素时测量阴影根内的元素,因为在加载和应用样式表后,“真实”尺寸是已知的,我不知道什么时候会发生(也许ResizeObserver 在实施时会有所帮助吗?)
谁能想出一个巧妙的方法来解决这些问题(无需手动内联样式或使用构建步骤)?我的担心有意义吗?考虑到<head> 块渲染中的<link rel="stylesheet> 和此功能应该与此类似,是否可以将其视为一个错误?

【问题讨论】:

    标签: styles web-component shadow-dom


    【解决方案1】:

    如果你想避免 FOUC,你应该隐藏元素直到样式被应用。当你使用fetchXMLHttpRequest,或<link onload=...>时,你可以知道你的样式何时加载。

    关于维度问题,它不是 Shadow DOM 特有的,而是 CSS 架构的结果。

    无论如何,通常建议设置元素的 with 和 height 以避免 FOUC,同时也可以避免整页重绘,从而加快渲染速度。

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

    【讨论】:

    • 暂时使用linkElement.onload = () => ... :)
    • 我们真正需要的是让 加载事件冒泡到影子根上的加载处理程序。可悲的是,我发现的唯一方法是在构造函数中为每个手动添加负载侦听器,然后在它们全部返回时调度自定义加载事件。
    【解决方案2】:

    这是一些使用 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; 这隐藏了shadow DOM的内部内容。

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

    只有在 CSS 加载后,我的元素才可见。

    作为另一种选择,您还可以在您的 &lt;link&gt; 标签上放置一个 onload 事件处理程序:

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

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

    【讨论】:

      猜你喜欢
      • 2014-10-11
      • 2019-07-21
      • 1970-01-01
      • 2013-07-06
      • 2015-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-20
      相关资源
      最近更新 更多