Статьи

Добавьте CSS Encapsulation в ваш любимый MV * Framework

Инкапсуляция 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. Это именно то, о чем я говорил в этой статье.

Прочитайте больше