Размер шрифта css-значения на основе Shadow DOM root

Краткое описание проблемы:

Я ищу способ установить значения размера шрифта (аналогично установке некоторого свойства в em-значение) в элементе внутри теневого dom относительно теневого хоста, независимо от размера шрифта родительского элемента в теневой дом. Что-то похожее на то, как работают rem-значения, но с тень как root вместо корня html.

Причиной этого является возможность масштабирования контента, например, виджета (который вставляется с теневым dom) в одном месте. Если свойства, основанные на размере шрифта, можно масштабировать таким образом, весь виджет можно масштабировать в каждом контексте, не устанавливая множество значений css, чтобы он соответствовал каждому местоположению.

Длинное описание проблемы:

Я ищу способ разметки текста (размер шрифта) на основе данного элемента в DOM, чтобы иметь возможность масштабировать различные части содержимого внутри этого элемента на основе этого элемента переноса (хорошо для виджетов и т.д.)., Это не только для фактического текста, часто бывает полезно наложить множество значений css на размер шрифта, поэтому элементы визуального макета масштабируются с размером текста (например, заполнение, радиус и тени). Если все основано на em-values, это может стать немного грязным. Если в элементе есть несколько уровней шрифта-шрифта, и я хочу увеличить первый уровень без изменения второго, мне нужно будет увеличить em первого уровня в dom, а уменьшить на любом элементе второго уровня (который рассчитывается на основе этого первого уровня), чтобы текст подэлементов оставался одного и того же размера. Это во многих случаях менее оптимально.

Хорошее решение базируется на Root EM (rem), поэтому изменение одного уровня dom не влияет на подэлементы. Однако, если я хочу увеличить/уменьшить размер всего текста внутри этих элементов упаковки (в одном месте), не затрагивая другие элементы на той же странице, мне нужно будет увеличить/уменьшить значения всех размеров шрифта для элементов внутри этого оберточного элемента.

Недавно я начал изучать веб-компоненты с помощью Shadow DOM и шаблонов. В тегах-шаблонах css инкапсулируется, что отлично, потому что дизайн этого окна/виджета/компонента может быть разработан самостоятельно, не задумываясь о остальной части дизайна, нежелательных унаследованных значениях и т.д. Наконец, хороший способ сделать независимые компоненты для создания веб-страницы. Но было бы здорово, если бы я мог установить своего рода размер шрифта для корневого шаблона для самого шаблона. Поэтому, если я установил некоторый элемент внутри шаблона как размер шрифта 2rem (или любой другой подобный блок), тогда размер шрифта этого элемента будет равным 2x размеру корневого шрифта шаблона, независимо от размера шрифта в хосте элемент, в котором используется этот шаблон, и независимо от размера шрифта корня страницы (элемент html).

Когда я тестировал с помощью rem-значений внутри шаблона, он все еще основывался на размере шрифта root (html tag). Я также тестировал vw-значения, но это также основано на всем видовом экране, а не только на инкапсулированной области (теневой корень).

Я просмотрел множество статей о 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>

Это даст одно место (корневая часть вышеприведенного примера), чтобы управлять всеми относительными размерами шрифтов внутри этого компонента, а затем его можно объединить с не относительными размерами шрифта, такими как px или другими нормальными значениями css.

Ответы

Ответ 1

после просмотра документов веб-компонентов. Ваш пример того, что вы хотите сделать, не похож на веб-компонент. см. следующий пример веб-компонента.

в заголовке вашего документа у вас будет

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

в теле, которое у вас было бы

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

ваш веб-компонент будет иметь свой собственный файл.

<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>

Ваш пример того, что вы пытаетесь сделать и что вы действительно хотите, не соответствует. После нескольких ответов от вас я вижу, что вы хотите делать веб-компоненты. Это не то, что вы показываете в своем примере того, что вы пытаетесь сделать. Также этот код работает только в том случае, если вы включили правильные флаги в веб-браузере Chrome Canary. По умолчанию он не будет работать, и пользователь должен включить правильные настройки бета-версии для работы. Даже в интранете я бы не стал рекомендовать использовать веб-компоненты, но по-прежнему очень бета-версии, и у вас будет много проблем, поддерживающих это со многими внутренними пользователями. Пользователи постоянно находят способы reset настроек в браузере. Пользователи сломают все новое и блестящее.

Ответ 2

Селектор :host стилирует элемент, в котором размещается теневой корень. Это имеет тот же эффект, что и стиль элемента-хозяина с внешними css/styles (например, <div id="rootelm" style="font-size:20px"></div> для примера ниже). Если вы используете :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, чтобы размер шрифта был относительно элемента, в котором размещен теневой корень. Script воспроизводится ниже.

    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();
    }

Ответ 3

Проблема заключается в том, что вы вызываете объект DOM "теневым корнем", что у вас есть другой элемент DOM, который является ветвью корня. шаблон тега не имеет особого значения в HTML5. Нет такой вещи, как "теневой корень". Существует несколько способов ввода документа в другой документ. используя iframes, теги объектов, теги элементов. Я не рекомендую делать какие-либо из них, так как это ломает способность документа правильно обрабатываться вместе с длинным списком других будущих проблем, которые у вас будут.

Ваш лучший вариант - создать элемент DOM в CSS, который имеет базовые атрибуты, с которыми вы хотите начать, и использовать css для создания подэлементов этого элемента в зависимости от вашего css. Вы можете создать шаблон элемента DOM в css и дать ему базовый стиль.

Файл 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>

это установит любой элемент DOM с именем template в размер шрифта 20px, и все элементы sub dom будут меняться в зависимости от родительского элемента. также старайтесь не использовать встроенный стиль для элементов, селектор css может сделать для вас много работы.