Я начинаю использовать пользовательские элементы, и одну вещь, которую я не могу понять, - это совместное использование стилей. Например, если у меня есть 2 пользовательских элемента, <element-1>
и <element-2>
, каждый из которых содержит <button>
, и я хочу, чтобы все кнопки имели определенный стиль, например, font-size:20px
.
Варианты, которые я рассмотрел:
Используйте пользовательский элемент <stylized-button>
вместо <button>
в пользовательских элементах. Это проблематично, когда внешний источник <element-1>
. Также проблематично, если вам нужны другие стили (например, color:red
) только для кнопок <element-1>
, а не для кнопок <element-2>
.
Насколько я могу судить из документации на полимер [1], у полимера также нет решения для этого.
/dead/
и :shadow
показались многообещающими, но больше не поддерживаются.
Аналогично @apply
[2] показалось многообещающим, но предложение было снято.
::part
и ::theme
[3] кажутся еще более многообещающими, но пока не поддерживаются.
Используйте js для поддержки ::part
и ::theme
[4]. я думаю, что это было бы очень хрупким без сглаживания всех случаев.
Явно добавьте общий стиль к каждому пользовательскому элементу.
class Element1 extends HTMLElement {
constructor() {
this.shadowRoot.addElement(sharedStyle);
}
}
Это кажется очень ограниченным и ручным. Также может повлиять на производительность? Также проблематично, если вы используете внешний источник <element-1>
.
Прямо сейчас, я думаю, что # 5 может быть лучшим, так как кажется наиболее общим / простым в использовании без создания специально для него, плюс это сделает переход на № 4 тривиальным, когда он будет реализован. Но мне интересно, есть ли другие подходы или предложения?
[1] https://www.polymer -project.org / 3.0 / docs / devguide / style-shadow-dom
[2] http://tabatkins.github.io/specs/css-apply-rule/
[3] https://meowni.ca/posts/part-theme-explainer/
[4] Наивная реализация и пример ее использования: https://gist.github.com/mahhov/cbb27fcdde4ad45715d2df3b3ce7be40
реализация:
document.addEventListener('DOMContentLoaded', () => {
// create style sheets for each shadow root to which we will later add rules
let shadowRootsStyleSheets = [...document.querySelectorAll('*')]
.filter(element => element.shadowRoot)
.map(element => element.shadowRoot)
.map(shadowRoot => {
shadowRoot.appendChild(document.createElement('style'));
return shadowRoot.styleSheets[0];
});
// iterate all style rules in the document searching for `.theme` and `.part` in the selectors.
[...document.styleSheets]
.flatMap(styleSheet => [...styleSheet.rules])
.forEach(rule => {
let styleText = rule.cssText.match(/\{(.*)\}/)[1];
let match;
if (match = rule.selectorText.match(/\.theme\b(.*)/))
shadowRootsStyleSheets.forEach(styleSheet => styleSheet.addRule(match[1], styleText));
else if (match = rule.selectorText.match(/\.part\b(.*)/))
shadowRootsStyleSheets.forEach(styleSheet => styleSheet.addRule(`[part=${match[1]}]`, styleText));
});
});
и использование:
<style>
.my-element.part line-green {
border: 1px solid green;
color: green;
}
.theme .line-orange {
border: 1px solid orange;
color: orange;
}
/*
must use `.part` instead of `::part`, and `.theme` instead of `::theme`
as the browser prunes out invalid css rules form the `StyleSheetList`'s.
*/
</style>
<template id="my-template">
<p part="line-green">green</p>
<p class="line-orange">orange</p>
</template>
<my-element></my-element>
<script>
customElements.define('my-element', class extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
const template = document.getElementById('my-template').content.cloneNode(true);
this.shadowRoot.appendChild(template);
}
});
</script>