基于 Shadow DOM 根的字体大小 css-values

Posted

技术标签:

【中文标题】基于 Shadow DOM 根的字体大小 css-values【英文标题】:Font size css-values based on Shadow DOM root 【发布时间】:2014-09-17 04:26:48 【问题描述】:

问题的简短描述:

我正在寻找一种方法来在相对于影子主机的影子 dom 内的元素中设置基于字体大小的值(类似于将某些属性设置为 em 值),而不管影子王国。类似于 rem-values 的工作方式,但使用阴影作为根而不是 html 根。

这样做的原因是能够像小部件一样在一个地方缩放内容(即插入 shadow dom)。如果可以通过这种方式缩放基于字体大小的属性,则可以在每个上下文中缩放整个小部件,而无需设置大量 css 值以使其适合每个位置。

问题的详细描述:

我正在寻找一种基于 DOM 中的给定元素来调整文本大小(字体大小)的方法,以便能够基于此包装元素缩放该元素内的不同内容部分(适用于小部件等) .这不仅适用于实际文本,通常有很多基于字体大小的 css 值是好的,因此元素的视觉布局会随着文本的大小(如填充、边框半径和阴影)缩放。如果一切都基于 em 值,这可能会有点混乱。如果元素内部有多个字体大小继承级别,并且我想放大第一级而不更改第二级,我需要放大 dom 中第一级的 em,同时减少任何第二级元素(即根据第一级计算)使子元素的文本保持相同的大小。在许多情况下,这不是最佳的。

一个不错的解决方案是基于根 EM (rem),因此更改 dom 的一个级别不会影响子元素。但是,如果我想在不影响同一页面上的其他元素的情况下放大/缩小此包装元素内的所有文本的大小(在一个地方),我需要放大/缩小所有字体大小的 rem 值该包装元素内的元素。

我最近开始研究带有 Shadow DOM 和模板的 Web 组件。在模板标签中,封装了 css,这非常棒,因为该框/小部件/组件的设计可以自行设置样式,而无需考虑其他设计、不需要的继承值等。最后是制作独立组件来构建网页的好方法。但是,如果我可以为模板本身设置一种模板根字体大小,那就太好了。因此,如果我将模板内的某些元素设置为 font-size 2rem (或任何其他类似单位),那么该元素的 font-size 将是模板的根字体大小的 2 倍,无论主机中的字体大小如何使用该模板的元素,并且与页面根目录(html 元素)的字体大小无关。

当我在模板中使用 rem-values 进行测试时,它仍然基于页面根(html 标记)字体大小。我还测试了 vw-values,但这也是基于整个视口,而不仅仅是封装区域(阴影根)。

我查看了很多关于 Shadow DOM 的文章,但我无法找到任何解决此问题的方法。有什么建议吗?

会发生什么(简化示例):

<html style="font-size: 16px">
    <body style="font-size: 12px">
        I am 12px large
        
        <!-- em, based on the body in this case -->
        <div style="font-size: 1.5em">
            I am 18px large
            
            <div style="font-size: 1.5em">
                I am 27px large
            </div>
        </div>
        
        <!-- rem, based on the styles of the html element -->
        <div style="font-size: 1.5rem">
            I am 24px large
            
            <div style="font-size: 1.5rem">
                I am 24px large
            </div>
        </div>
    </body>
</html>

我想要什么:

<html style="font-size: 16px">
    <body style="font-size: 12px">
        I am 12px large

        <div style="font-size: 1.5em">
            I am 18px large
        </div>
        <div style="font-size: 1.5rem">
            I am 24px large
        </div>

        <template> <!-- Simulates that it is inserted into the DOM by using shadow elements -->
            <style>
                :root /* Or something like that */
                    font-size: 20px; /* This is the simulated "template root fontsize" */
                
            </style>
            
            <div style="font-size: 1.5trem"> <!-- Based on the template root fontsize -->
                I am 30px large
                
                <div style="font-size: 2trem">
                    I am 40px large
                </div>
            </div>

        </template>
    </body>
</html>

这将提供一个位置(上面示例的 :root 部分)来控制该组件内的所有相对字体大小,然后可以将其与非相对字体大小(如 px 或其他普通 css 值)结合使用。

【问题讨论】:

我也想要这个。您确实需要能够根据容纳您的组件的容器的大小来确定所有大小。我发布了similar question。 【参考方案1】:

在查看 Web 组件文档之后。您想要做的事情的示例看起来与 Web 组件完全不同。请参阅以下 Web 组件示例。

在您的文档的标题中

<link rel="import" href="web_component_name.html"></link>

在你的身体里

<my-web-component>Bob</my-web-component>

您的 Web 组件将拥有自己的文件。

<html>
  <template id="nameTagTemplate">
    <style>
    .outer 
      border: 2px solid brown;
      border-radius: 1em;
      background: red;
      font-size: 20pt;
      width: 12em;
      height: 7em;
      text-align: center;
    
    .boilerplate 
      color: white;
      font-family: sans-serif;
      padding: 0.5em;
    
    .name 
      color: black;
      background: white;
      font-family: "Marker Felt", cursive;
      font-size: 45pt;
      padding-top: 0.2em;
    
    </style>
    <div class="outer">
      <div class="boilerplate">
        Hi! My name is
      </div>
      <div class="name">
        <content></content>
      </div>
    </div>
  </template>
  <script>
    var importDoc = document.currentScript.ownerDocument;
    var nameBadgePrototype = Object.create(HTMLElement.prototype);
    nameBadgePrototype.createdCallback = function() 
      var shadow = this.createShadowRoot();
      var template = importDoc.querySelector('#nameTagTemplate');
      shadow.appendChild(template.content.cloneNode(true));
    ;
    document.registerElement("my-web-component", 
      prototype: nameBadgePrototype
    );
  </script>
</html>

您尝试做的事情与您真正想要做的事情的示例不匹配。经过您的一些回复,我看到您想做 Web 组件。这不是你在你的例子中展示的你正在尝试做的事情。此外,仅当您在 Chrome Canary 网络浏览器中启用了正确的标志时,此代码才有效。默认情况下它不会工作,需要用户启用正确的 beta 设置才能工作。即使在 Intranet 中,我也不建议使用 Web 组件,因为它仍然处于测试阶段,并且与许多内部用户一起维护它会遇到很多问题。用户总是想方设法重置浏览器上的设置。用户打破一切新的和闪亮的。

【讨论】:

我的示例不是经过测试的代码,它(如前所述)是一个伪示例,以显示我想要实现的目标。我已经对 Web 组件代码(包括 Shadow DOM、模板和自定义元素)进行了实际测试,并且在 Chrome(非测试版)中工作得非常好。并且存在适用于所有主要浏览器的 polyfill。我(如前所述)知道 WC 尚未得到广泛支持,但这与本次调查无关。关键是要找出是否有任何方法可以将类似 em 的字体大小值基于影子根的字体大小,就像使用 rem 为文档根一样。供将来使用。 我同意 WC 还不适合生产环境。但是,它的支持范围比您所说的要广泛。 Chrome 25+ 支持 Shadow dom(从 35 开始使用 webkit-prefix),v37 是当前最新的非 beta 版本。 Chrome 33+ 支持自定义元素。 Chrome 26+ 和 Firefox 22+(FF 31 是最新的)支持 HTML 模板。 Polymer 项目发布了 javascript-polyfills,使这些功能在所有主要浏览器的两个最新版本中都能正常工作。我自己还没有测试过 polyfill。 我将不得不对此进行测试,因为我所阅读的所有内容都表明它在 chrome 中仍处于测试阶段,并且尚未在 Firefox 中实现。如果您有当前状态的来源,请在本网站上发布它们与 WC 相关。 我从 caniuse.com 获得了支持不同技术的版本:caniuse.com/#feat=shadowdomcaniuse.com/#feat=custom-elementscaniuse.com/#feat=template 以及来自 Polymer 项目的有关 Web 组件 polyfill 的一些信息:polymer-project.org/resources/compatibility.html 以及一些一般信息关于 Polymer 平台:polymer-project.org/docs/start/platform.html【参考方案2】:

问题是您将 DOM 对象称为“影子根”,而您所拥有的是另一个 DOM 元素,它是根的一个分支。标签模板在 HTML5 中没有特殊含义。没有“影子根”这样的东西。有多种方法可以将一个文档注入到另一个文档中。使用 iframe、对象标签、元素标签。我不建议这样做,因为它会破坏文档正确解析的能力以及您将来会遇到的一长串其他问题。

您最好的选择是在 CSS 中创建一个 DOM 元素,该元素具有您想要开始的基本属性,并使用 css 根据您的 css 更改该元素的子项。您可以在 css 中创建一个 DOM 元素模板并为其提供基本样式。

CSS 文件

template
    font-size: 20px;

html文件

<template>
   <div style="font-size: 1.5trem">
       I am 30px large
       <div style="font-size: 2trem">
           I am 40px large
       </div>
   </div>
</template>

这会将任何名为 template 的 DOM 元素设置为 font-size 20px 并且所有子 dom 元素将根据父元素进行更改。也尽量不要在元素上使用内联样式,css 选择器可以为你做很多工作。

【讨论】:

看来(如果我错了,请纠正我)您可能不熟悉称为 Web 组件的 HTML5 技术,它是一组标准,包括 Shadow DOM、模板等。这些技术的目的正是避免通过嵌入、iframe 和类似方式插入内容。本页解释了 Web Components 的概念:css-tricks.com/modular-future-web-components Shadow DOM 仅适用于 Chrome、Opera、android 浏览器、Android 版 Chrome。所以它不能在 IE、FireFox、Safari、ios safari、Opera mini 中运行。当您知道如何在元素上设置 css 以及属性如何遍历 DOM 树时,“Shadow DOM”几乎没有用处。我的示例设置了字体大小,因此模板 DOM 元素的所有子元素将从模板完全继承您在问题中想要的输出。 Shadow DOM 状态也是“正在考虑中”,它不是一成不变的。 基于问题 Web Components is not required and is overkill。简单的 CSS 解决方案可以在不使用不完全支持的功能的情况下解决此问题。 我不同意。我曾多次在混乱的 CSS 汤中工作,不得不花费数小时清理,因为随着时间的推移,CSS 通常会变得非常混乱。 CSS封装是我期待多年的东西。唯一缺少的 (imo) 是影子根 em。我知道 WC 尚未得到广泛支持,但它可用于内部系统,希望能及时用于所有网站。 我发布了一个新答案,向您展示如何使用 Web 组件。请查看它并注意,是的,该代码有效,但我仍然不建议在实时环境中使用 beta 代码,甚至是 Intranet。很多不好的事情会发生,很多事情会改变。将 Web 组件用作为公众使用做准备的测试,而不是为客户使用。【参考方案3】:

:host 选择器对承载影子根的元素进行样式设置。这与使用外部 css/styles 设置宿主元素的样式效果相同(例如,&lt;div id="rootelm" style="font-size:20px"&gt;&lt;/div&gt; 用于下面的示例)。如果您使用 :host 而不是假的 :root 选择器,它应该可以按预期工作。这是一个例子:

<!DOCTYPE HTML>
<html style="font-size: 16px">
    <body style="font-size: 12px">
        I am 12px large

        <div style="font-size: 1.5em">
            I am 18px large
        </div>
        <div style="font-size: 1.5rem">
            I am 24px large
        </div>

        <div id="rootelm"></div>

        <template id="testtemplate">
            <style>
                :host /* Our new selector */
                    font-size: 20px; /* This is the "template root fontsize" */
                
            </style>
            <div style="font-size: 1em">
                I am 20px large
                <!-- Absolute size for comparison -->
                <div style="font-size: 20px">I am 20px large as well</div>
                <div style="font-size: 2em">
                    I am 40px large
                    <!-- Absolute size for comparison -->
                    <div style="font-size: 40px">I am 40px large as well</div>
                </div>
            </div>
        </template>

        <script>
            var shadow = document.querySelector('#rootelm').createShadowRoot();
            var template = document.querySelector('#testtemplate');
            shadow.innerHTML = template.innerHTML;
        </script>
    </body>
</html>

plunker

编辑: 使用 JS 更新了 plunker,以使字体大小相对于承载影子根的元素。脚本转载如下。

    function updateFontSizes() 
        //Get root element. If you had more than one, you could use querySelectorAll(), and loop over the resulting array
        var rootElm = document.querySelector("#rootelm");
        /*Get styled elements. If you were using external styles instead of inline,
          you'd have to use getComputedStyle or another method of getting CSS */
        var styledElms = rootElm.shadowRoot.querySelectorAll('[style]');
        //Get the root font size (the font size applied to the element containing the template)
        var rootFontSize = window.getComputedStyle(rootElm, null).getPropertyValue("font-size");
        //Format root fontsize. If you are using different units, change the string in IndexOF
        rootFontSize = parseFloat(rootFontSize.substring(0, rootFontSize.indexOf('px')).trim());
        for (var i = 0; i < styledElms.length; i++) 
            //Get relative font size index of units
            var unitIndex = styledElms[i].style.fontSize.indexOf('rem');
            //Get custom attribute that we will fill later
            var oldFS = styledElms[i].getAttribute("oldfs");
            //if font-size contains the relative units
            if (unitIndex > -1) 
                //Store relative font size in custom attribute
                styledElms[i].setAttribute("oldfs",styledElms[i].style.fontSize);
                //Set fontsize to relative font size * computed root font size
                styledElms[i].style.fontSize = parseFloat(styledElms[i].style.fontSize.substring(0, unitIndex).trim()) * rootFontSize + "px";
            
            //If font size doesn't contain the relative units, and we stored the old relative font size
            else if (oldFS !== null) 
                //Set the fontsize to old relative font size * computed root font size
                styledElms[i].style.fontSize = parseFloat(oldFS.substring(0, oldFS.indexOf('rem')).trim()) * rootFontSize + "px"
            
        
    

    //Media query listeners
    //See https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Testing_media_queries for more info
    var mql = window.matchMedia("(max-width: 500px)");
    mql.addListener(handleMediaQuery);
    handleMediaQuery(mql);

    function handleMediaQuery(mql) 
        updateFontSizes();
    

【讨论】:

这并不能解决问题。我想根据阴影根设置基于字体大小的值,而不考虑父级。然后我可以使用媒体查询控制模板根,相对于该内容缩放内容,而不是对所有子元素进行媒体查询。 em 值基于 html 根目录上的 parent 和 rem 值。在我的示例中,html 字体大小为 16,模板根为 20。我希望能够将一个元素设置为 1.5 倍模板根字体大小(例如我的示例中的“我是 30px 大”)和一个子元素其中是 2x 模板根(40px),无论父级(即 30px)。 @henit 我不相信仅使用 css 是可能的,但可以使用 Javascript 来完成。我已经用一个脚本更新了答案和plunker,该脚本将 rem 字体大小更改为相对于阴影根。弹出 plunker 并调整窗口大小以查看媒体查询更改。

以上是关于基于 Shadow DOM 根的字体大小 css-values的主要内容,如果未能解决你的问题,请参考以下文章

Shadow dom 中的 FontAwesome svg

hbuilder那个属性可以为div元素添加阴影边框

如何使用 Selenium Java 框架自动化 Shadow DOM

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

Shadow DOM 元素可以继承 CSS 吗?

CSS文本字体个别属性效果对比