Инкапсуляция CSS — одна из моих любимых функций на веб-платформе. И хотя сегодня он не поддерживается большинством браузеров, его довольно легко обработать.
пример
Предположим, у нас есть блог, где мы используем какой-то компонент комментариев.
<p>One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin.</p> <comments id="some-id"></comments>
Где шаблон компонента комментариев выглядит следующим образом:
<div ng-repeat="comment in comments"> <span>{{comment.author}}</span> <p>{{comment.description}}</p> <button>Reply</button> </div>
Поскольку мы имеем дело с браузерами, которые не поддерживают Shadow DOM и CSS Encapsulation, DOM в конечном итоге будет выглядеть так
<p>One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin.</p> <comments id="some-id"> <div ng-repeat="comment in comments"> <span>{{comment.author}}</span> <p>{{comment.description}}</p> <button>Reply</button> </div> </comments>
Я использую синтаксис в угловом стиле, но это не обязательно. Все, о чем я буду говорить, может быть применено к любой среде, в которой есть понятие компонента.
Обратите внимание, что мы используем тег p как в компоненте комментариев, так и в приложении, и нам бы хотелось по-разному их стилизовать.
/* application.css */ p { font-size: 12px; } /* comments.css */ p { font-size: 10px; background-color: LightCyan; }
Если мы включим два элемента стиля на страницу, оба тега p будут иметь цвет фона LightCyan.
Просто добавьте Scoping
Самый простой способ эмулировать инкапсуляцию CSS — это то, что все в Интернете делали годами: поместите стили в comments.css . Нам просто нужно написать простой компилятор, который будет компилировать
p { font-size: 10px; background-color: LightCyan; }
в
comments p { font-size: 10px; background-color: LightCyan; }
Компилятор просто добавляет имя компонента к каждому селектору CSS. Это решение работает в простых случаях, таких как приведенный выше, но разваливается, когда вы начинаете работать с вложенными компонентами.
Вложенные компоненты
Давайте усложним наш пример, представив новый компонент с именем author .
<comments id="some-comments-id"> <div ng-repeat="comment in comments"> <author name="comment.author"></author> <p>{{comment.description}}</p> <button>Reply</button> </div> </comments>
Где шаблон компонента автор выглядит следующим образом:
<p>{{name}}</p>
ДОМ:
<comments id="some-comments-id"> <div ng-repeat="comment in comments"> <author name="comment.author"> <p>{{name}}</p> </author> <p>{{comment.description}}</p> <button>Reply</button> </div> </comments>
Теперь у нас есть проблема, потому что селектор комментариев p соответствует p в шаблоне автора . Таким образом, добавление области видимости недостаточно хорошо — нам нужен другой подход.
Пометка узлов DOM
Этот метод более сложный и требует от нас компиляции не только CSS компонента, но и его шаблона.
Сначала давайте скомпилируем шаблон.
<comments id="some-comments-id"> <div ng-repeat="comment in comments" comments=""> <author comments="">{{comment.author}}</author> <p comments="">{{comment.description}}</p> <button comments="">Reply</button> </div> </comments>
Как видите, мы добавили атрибут comments ко всем элементам шаблона. Если вы используете шаблоны на основе DOM, вы можете написать однострочную функцию, которая будет выполнять эту компиляцию, используя querySelectAll («*») .
Во-вторых, давайте скомпилируем CSS.
p[comments] { font-size: 10px; background-color: LightCyan; }
Мы добавили суффикс атрибута к селектору. Теперь правило цвета фона применяется только к тегу p в шаблоне комментариев.
Пользовательские элементы не требуются
В примере используются пользовательские элементы, но вы не обязаны. Просто используйте один и тот же токен при компиляции HTML и CSS. Так что, если у вас есть приложение Backbone, которое семантически имеет компоненты, но использует для их реализации обычные div, скомпилированный шаблон может выглядеть так
<div id="some-comments-id"> <div ng-repeat="comment in comments" CommentsComponent=""> <div CommentsComponent="">{{comment.author}}</div> <p CommentsComponent="">{{comment.description}}</p> <button CommentsComponent="">Reply</button> </div> </div> p[CommentsComponent] { font-size: 10px; background-color: LightCyan; }
Если вы решили предварительно скомпилировать свои шаблоны и CSS, вы можете использовать соглашение об именах файлов.
Используйте его в своем любимом MV * Framework
Все, о чем я говорил, не зависит от фреймворка и может использоваться с любой библиотекой, если есть компоненты, похожие на шаблоны, которые имеют шаблоны и CSS. Подключение в Backbone или Angular не должно превышать нескольких десятков строк кода.
Должен ли я написать компилятор CSS?
Компилятор CSS, который добавляет суффикс атрибута ко всем простым селекторам, может быть довольно сложным. Как сложно? В основном это зависит от того, какие функции CSS вы хотите поддерживать: хотите ли вы поддерживать медиа-запросы? Хотите ли вы поддерживать псевдо-селекторы, такие как : host и : host-content ? Это, однако, не так плохо, как может показаться. Вы можете написать реализацию, которая обрабатывает большинство случаев в нескольких сотнях строк JavaScript.
Сказав это, вы не должны это делать. Просто используйте компилятор, который поставляется с Platform.js. Это именно то, о чем я говорил в этой статье.