logo

Paleta - Deixe colorido🎨

Palette — Construtor visual de páginas. Não precisa ser designer.

Demonstração ao vivo Baixar Palette

Scroll

Adicionando folhas de estilo (CSS) e JavaScript (JS) a um tema Drupal 8

03/05/2020, by maria

Esta documentação é para temas. Информацию о модулях смотрите в разделе Добавление таблиц стилей (CSS) и JavaScript (JS) в модуль Drupal 8.

В Drupal 8 таблицы стилей (CSS) и JavaScript (JS) загружаются через одну и ту же систему для модулей (кода) и тем для всего: библиотек ресурсов.

Для ясности, эти инструкции предназначены ТОЛЬКО для работы в темах и не применяются в модулях.

Princípio geral: assets (CSS/JS) só carregam se você disser ao Drupal que devem — ele não carrega tudo em cada página pois isso degrada a performance da interface.

Отличия от Drupal 7

Seis diferenças importantes em relação ao D7:

 

Процесс

Чтобы загрузить ресурсы CSS или JS:

Определение библиотеки

Определите все свои библиотеки ресурсов в файле *.libraries.yml в папке вашей темы. Если ваша тема называется fluffiness, имя файла должно быть fluffiness.libraries.yml. Каждая «библиотека» в файле - это запись, детализирующая файлы CSS и JS (ресурсы), например:

# fluffiness.libraries.yml
cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}

В этом примере JavaScript: cuddly-slider.js и CSS cuddly-slider.css находятся в соответствующих директориях js и css вашей директории.

Обратите внимание, что хотя этот пример демонстрирует добавление одного файла css и js + jquery. При определении библиотек доступно значительно больше опций. Их можно найти в разделе «Определение библиотек: параметры и детали».

Включение Jquery в вашу библиотеку

Помните, Drupal 8 больше не загружает jQuery на все страницы по умолчанию, поэтому, например, если cuddly-slider требуется JQuery, вы должны объявить зависимость от базовой библиотеки, содержащей jQuery (ядро Drupal предоставляет jQuery, а не модуль или тему). Объявите зависимость с именем расширения, за которым следует косая черта, за которой следует имя библиотеки, в данном случае core/jquery. Если другой библиотеке требуется cuddly-slider, она объявляет: fluffiness/cuddly-slider, имя темы, за которым следует имя библиотеки. Вы не можете объявить отдельный файл как зависимость, только библиотеку

Таким образом, чтобы сделать jQuery доступным для cuddly-slider, мы обновим приведенное выше:

# fluffiness.libraries.yml
cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery

Объявление зависимостей

Чтобы объявить зависимость, необходимая библиотека объявляется в форме ресурса/библиотеки. Для основных библиотек ресурс является основным, в то время как для других это имя модуля или темы. Поэтому, если new_library зависит от jQuery от ядра, my_library, объявленного в my_theme, и my_library, объявленного в my_module, вы должны объявить зависимости как:

# fluffiness.libraries.yml
new_library:
  js:
    js/new_libary.js: {}
  dependencies:
    - core/jquery
    - my_module/my_library
    - my_theme/my_library

Имена модулей и тем обеспечивают пространство имен для библиотек с одинаковыми именами.

Присоединение библиотеки ко всем страницам

В большинстве тем используется библиотека ресурсов global-styling для таблиц стилей (файлов CSS), которые необходимо загружать на каждую страницу, где тема активна. Также возможно сделать с JS через библиотеку ресурсов global-scripts

# fluffiness.libraries.yml (multiple libraries can be added to a libraries.yml file, these would appear below the cuddly-slider libraries added earlier)
global-styling:
  version: 1.x
  css:
    theme:
      css/layout.css: {}
      css/style.css: {}
      css/colors.css: {}
global-scripts:
  version: 1.x
  js: 
    js/navmenu.js: {}   

Para ficarem disponíveis em toda parte do tema global-styling/global-scripts acrescentam-se ao info.yml do seu tema (aqui fluffiness.info.yml)

#fluffiness.info.yml
name: Fluffiness
type: theme
description: 'A cuddly theme that offers extra fluffiness.'
core: 8.x
# by adding global-styling and global-scripts here, the css/js files in the library become 
# available to every page presented by the theme
libraries:
  - fluffiness/global-styling
  - fluffiness/global-scripts
base theme: classy
regions:
  header: Header
  content: Content
  sidebar_first: 'Sidebar first'
  footer: Footer

Присоединение библиотеки через шаблон Twig

Вы можете присоединить библиотеку ресурсов к шаблону Twig, используя функцию attach_library() в любом файле *.html.twig, например так:

{{ attach_library('fluffiness/cuddly-slider') }}
<div>Some fluffy markup {{ message }}</div>

Присоединение библиотеки к подмножеству страниц

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

Тема может сделать это, реализовав функцию THEME_preprocess_HOOK() в файле .theme, заменив «THEME» на машинное имя вашей темы, а «HOOK» - на машинное имя хука темы.

Например, если вы хотите присоединить JavaScript к странице обслуживания, часть «HOOK» - это «maintenance_page», и ваша функция будет выглядеть следующим образом:

function fluffiness_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] = 'fluffiness/cuddly-slider';
}

Вы можете сделать нечто подобное для других хуков тем, и, конечно, ваша функция может содержать логику - например, чтобы определить, какой блок предварительно обрабатывается в хуке «блок», какой тип узла для хука «узел» и т. д.

Важная заметка! В этом случае вам необходимо указать метаданные для кеширования, соответствующие вашему состоянию! Приведенный выше пример работает безоговорочно, поэтому метаданные для кеширования не нужны. Наиболее распространенный вариант использования, вероятно, когда вы присоединяете некоторую библиотеку активов на основе текущего маршрута:

function fluffiness_preprocess_page(&$variables) {
  $variables['page']['#cache']['contexts'][] = 'route';
  $route = "entity.node.preview";
  if (\Drupal::routeMatch()->getRouteName() === $route) {
    $variables['#attached']['library'][] = 'fluffiness/node-preview';
  }
}

Определение библиотек: параметры и детали

Добавление свойств во включенные css/js

Свойства добавляются в фигурные скобки после каждого файла, добавляемого в файл THEMENAME.libraries.yml вашей темы.

Свойства CSS

Следующие свойства являются необязательными и применяются для каждого актива CSS.

attributes Необязательные атрибуты. Известен вариант использования Bootstrap CDN.
{ attributes: { crossorigin: anonymous } }

 

browsers Загрузите ресурс условно на основе браузера. Обратите внимание, что этот метод использует условные комментарии, которые не поддерживаются в версиях IE10 и выше.
{ browsers: { IE: 'lte IE 9', '!IE': false } }

 

group Активы агрегированы по группам.
По умолчанию: группа SMACSS, в которую помещен актив.

Редко используется

 

media Тип носителя.
{ media: print }

 

minified Является ли актив уже минимизированным.
По умолчанию: false
{ type: external, minified: true }

 

preprocess Должны ли активы быть агрегированы.
По умолчанию: правда
{ preprocess: false }

 

type Источник актива.
По умолчанию: файл
{ type: external, minified: true }

 

weight Корректирует порядок относительно других активов (в пределах той же группы SMACSS).
По умолчанию: 0. Используйте числовое значение от -50 до +50.
{ weight: 1 }

 

 

JS свойства

Следующие свойства являются необязательными и применяются для каждого актива JS.

attributes Дополнительные атрибуты скрипта.
{ type: external, attributes: { async: true } }

 

browsers Загрузите ресурс условно на основе браузера. Обратите внимание, что этот метод использует условные комментарии, которые не поддерживаются в версиях IE10 и выше.
{ browsers: { IE: 'lte IE 9', '!IE': false } }

 

preprocess Должны ли активы быть агрегированы.
По умолчанию: правда
{ preprocess: false }

 

type Источник актива.
По умолчанию: файл
{ type: external, minified: true }

 

weight Не рекомендуется использовать зависимости вместо.
Регулирует порядок относительно других активов. Должен быть отрицательным.
{ weight: -1 }

 

 

Переопределение и расширение библиотек

Вы должны перейти к *.info.yml, чтобы переопределить библиотеки, определенные в *.libraries.yml. Они могут быть либо переопределены, либо расширены с помощью библиотек-переопределений или библиотек-расширений. Переопределения, которые вы добавляете в *.info.yml, будут унаследованы подтемами.

Свойство stylesheets-remove, используемое в файле *.info.yml, устарело и будет удалено в Drupal 9.0.x. Свойство stylesheets-override уже удалено.

libraries-override

Lógica a usar ao criar overrides:

  • Use o namespace original do módulo (ou core) como nome da library.
  • Use o caminho do override mais recente como chave.
  • Esse caminho deve ser o caminho completo até o arquivo.

Например:

libraries-override:
  contextual/drupal.contextual-links:
    css:
      component:
        /core/themes/stable/css/contextual/contextual.module.css: false

Aqui contextual/drupal.contextual-links é o namespace da library base e /core/themes/stable/css/contextual/contextual.module.css o caminho completo do override mais recente dessa library. Nesse caso o arquivo foi sobrescrito por false.

Importante notar que só a última parte é caminho real do filesystem; o resto refere-se a namespaces. As linhas css:/component: refletem структуру перезаписываемой библиотеки.

Ao usar lembre: depender do caminho do filesystem significa que mudanças na estrutura do site podem quebrá-lo; por isso existe issue sobre remover a dependência de caminho completo с помощью потоковых упаковщиков.

Outras formas de usar libraries-override para remover/substituir assets CSS/Javascript ou libraries inteiras herdadas pelo tema de módulos/temas:

libraries-override:
  # Replace an entire library.
  core/drupal.collapse: mytheme/collapse
  
  # Replace an asset with another.
  subtheme/library:
    css:
      theme:
        css/layout.css: css/my-layout.css

  # Replace an override asset from stable.
  contextual/drupal.contextual-toolbar:
    css:
      component:
        core/themes/stable/css/contextual/contextual.toolbar.css: css/contextual.toolbar.css

  # Replace a core module JavaScript asset.
  toolbar/toolbar:
    js:
      js/views/BodyVisualView.js: js/views/BodyVisualView.js

  # Remove an asset.
  drupal/dialog:
    css:
      theme:
        dialog.theme.css: false
  
  # Remove an entire library.
  core/modernizr: false
  
  # Replace very specific assets from a contributed module's library.
  # Note: The module's libraries available for overriding can be found in the module's *.libraries.yml file. In this example, you would find the libraries.yml file at the following location: /modules/contrib/webform/webform.libraries.yml 

  webform/webform.element.location.places:
    css:
      component:
        css/webform.element.location.places.css: css/my-themes-replacement-file.css
    js:
      js/webform.element.location.places.js: js/my-themes-replacement-file.js

libraries-extend

library-extends permite temas alterarem assets de library acrescentando assets dependentes do tema sempre que a library anexa-se.
library-extends определяются расширением библиотеки любым количеством других библиотек.

Ideal para estilizar alguns componentes diferentemente no tema sem fazê-lo num CSS global; ou seja ajustar aparência de componente sem carregar CSS para isso em todas as páginas.

# Estende drupal.user: acrescenta assets das user libraries do classy.
libraries-extend:
  core/drupal.user: 
    - classy/user1
    - classy/user2

Дополнительная настройка Javascript

Порядок загрузки активов

Как и следовало ожидать, порядок, в котором перечислены файлы, является порядком их загрузки. По умолчанию все ресурсы JS теперь загружаются в нижний колонтитул. JS для критических элементов пользовательского интерфейса, которые не могут быть показаны, если не запущен их соответствующий JS, могут быть загружены в заголовок, если необходимо, следующим образом:

js-header:
  header: true
  js:
    header.js: {}

js-footer:
  js:
    footer.js: {}

Defina header true indicando que os assets JS dessa library estão no critical path e devem carregar do header. Quaisquer dependências diretas/indiretas declaradas também carregam automaticamente do header — não precisa declará-las individualmente. É esse o sentido de critical path: quando asset declara-se no header é crítico para esse asset e todas suas dependências carregarem primeiro.

Присоединение настраиваемого JavaScript:

В некоторых случаях вы можете захотеть добавить JavaScript на страницу, которая зависит от некоторой вычисленной информации PHP.

В этом случае создайте файл JavaScript, определите и присоедините библиотеку, как и прежде, но также присоедините настройки JavaScript и попросите этот файл JavaScript прочитать эти настройки через drupalSettings (преемник Drupal 7's Drupal.settings). Однако, чтобы сделать drupalSettings доступным для нашего файла JavaScript, мы должны сделать ту же работу, что и для обеспечения доступности jQuery: мы должны объявить зависимость от него.

Так что тогда становится:

cuddly-slider:
  version: 1.x
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

и 

function fluffiness_page_attachments_alter(&$page) {
  $page['#attached']['library'][] = 'fluffiness/cuddly-slider';
  $page['#attached']['drupalSettings']['fluffiness']['cuddlySlider']['foo'] = 'bar';
}

Где 'bar' - это некоторое расчетное значение. (Обратите внимание, что метаданные для кеширования здесь также необходимы!)

Тогда cuddly-slider.js сможет получить доступ к settings.fluffiness.cuddlySlider.foo (и это будет === 'bar'):

(function ($, Drupal, drupalSettings) {

  'use strict';

  Drupal.behaviors.mybehavior = {
    attach: function (context, settings) {
      
      console.log(settings.fluffiness.cuddlySlider.foo);
      
    }
  };

})(jQuery, Drupal, drupalSettings);

Добавление атрибутов в элементы скрипта

Если вы хотите добавить атрибуты в тег скрипта, вам нужно добавить ключ атрибутов в JSON после URL скрипта. Внутри объекта, следующего за ключом атрибутов, добавьте имя атрибута, которое вы хотите отобразить в сценарии в качестве нового ключа. Значение для этого ключа будет значением атрибута. Если для этого значения установлено значение true, атрибут будет отображаться сам по себе без значения элемента.

Например:

https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap:
  type: external
  attributes:
    defer: true
    async: true
    data-test: map-link

Это приведет к следующей разметке:

<script src="https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap" async defer data-test="map-link"></script>

Встроенный JavaScript

Встроенный JavaScript крайне не рекомендуется. Рекомендуется поместить в файл JS, который вы хотите использовать inline, поскольку это позволяет кэшировать JavaScript на стороне клиента. Это также позволяет коду JavaScript быть просмотренным и написанным.

Встроенный JavaScript, который генерирует разметку
Это не рекомендуется и, как правило, не нужно. Поместите JavaScript в файл. Примерами этого являются реклама, кнопки общего доступа к социальным сетям, виджеты списков социальных сетей. Они используют встроенный JavaScript. Но это просто особый вид контента / разметки, поскольку они не предназначены для украшения контента сайта или его интерактивности, а для извлечения внешнего контента через JavaScript.

Вы хотите поместить их в пользовательский блок или даже непосредственно в шаблон Twig.

Например.:

<script type="text/javascript"><!--
ad_client_id = "some identifier"
ad_width = 160;
ad_height = 90;
//--></script>
<script type="text/javascript" src="http://adserver.com/ad.js"></script>
<a class="twitter-timeline" href="https://twitter.com/wimleers" data-widget-id="307116909013368833">Tweets by @wimleers</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

Встроенный JavaScript, который влияет на всю страницу

Использование любого встроенного JavaScript крайне не рекомендуется. Примерами встроенного JavaScript, который влияет на всю страницу, являются аналитика (например, Google Analytics) и службы размещенных шрифтов. Встроенный JavaScript, влияющий на всю страницу, может относиться к одной из двух категорий: front-end/styleling или логический.

В случае внешнего интерфейса / стиля (например, размещенные службы шрифтов) JS принадлежит к теме. Поместите JS прямо в ваш файл html.html.twig. В случае со шрифтами это также позволит вам правильно расположить его там, где вы получите лучший (и самый быстрый) интерфейс для конечного пользователя, поскольку он позволяет предотвратить FOUT (Flash Of Unstyled Text), пока шрифт все еще загрузка (шрифты, загруженные через JS, должны быть перечислены в HTML <HEAD> до CSS)!
(Подробнее об этом можно прочитать в отличной статье «Async Typekit & Micro-FOUT».)

В другом случае он принадлежит модулю, и для этого, пожалуйста, смотрите «Добавление таблиц стилей (CSS) и JavaScript (JS) в модуль Drupal 8».

Встроенный JavaScript в модуле интеграции

Использование любого встроенного JavaScript крайне не рекомендуется. Если вы можете использовать один из приведенных выше примеров, рассмотрите их, прежде чем пытаться сделать это.

Две вещи, которые следует учитывать при предоставлении поля, которое принимает встроенный JavaScript, предоставленный пользователем сайта:

1. Поле, форма или страница, которые принимают этот встроенный JavaScript, должны иметь прикрепленное разрешение.
Пример: MODULE.routing.yml

MODULE.settings:
  path: /admin/config/services/MODULE
  defaults:
    _title: 'MODULE settings'
    _form: \Drupal\MODULE\Form\MODULESettings
  requirements:
    _permission: 'administer site configuration'

2. Значение, если оно хранится в объекте конфигурации, должно оповещать систему рендеринга о ее CacheableMetadata, поэтому при его изменении кэш рендеринга элемента будет очищен / истек.
Пример: MODULES.module

<?php

/**
 * @file
 * Integrates MODULE in a Drupal site.
 */

use Drupal\Core\Render\Markup;

/**
 * Implements hook_page_bottom().
 */
function MODULE_page_bottom(array &$page_bottom) {
  $settings = \Drupal::config('MODULE.settings');
  $user = \Drupal::currentUser();
  $page_bottom['MODULE'] = [
    '#markup' => Markup::create($settings->get('js_code')),
    '#cache' => [
      'contexts' => ['user'],
      'tags' => ['user:' . $user->id()],
    ],
  ];
  // Add config settings cacheability metadata.
  /** @var Drupal\Core\Render\Renderer $renderer */
  $renderer = \Drupal::service('renderer');
  $renderer->addCacheableDependency($page_bottom['MODULE'], $settings);
}

CDN / внешние библиотеки

Возможно, вы захотите использовать JavaScript, который находится снаружи в CDN (сети доставки контента) - например, веб-шрифты обычно доступны только с использованием внешнего URL. Это можно сделать, объявив библиотеку внешней (указав type: external). Также полезно включить в определение некоторую информацию о внешней библиотеке.

(Обратите внимание, что это вообще не является хорошей идеей для библиотек загружаем с CDN; Во избежание этого, если возможно, он вводит больше точек отказа как производительность и стабильность с точки зрения безопасности, требует большего количества соединений TCP/IP должен быть установлен и, как правило, является в любом случае, не в кеше браузера. Однако сторонние библиотеки не должны размещаться на Drupal.org как часть вашего репо - см. Политику по сторонним библиотекам на Drupal.org для разъяснения политики.)

angular.angularjs:
  remote: https://github.com/angular
  version: 1.4.4
  license:
    name: MIT
    url: https://github.com/angular/angular.js/blob/master/LICENSE
    gpl-compatible: true
  js:
    https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }

Если вы хотите, чтобы ваш внешний файл запрашивался по тому же протоколу, что и страница, по которой запрашивается страница, укажите относящийся к протоколу URL:

js:
    //ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }

Или, если вы хотите добавить CSS, вот пример интеграции Font Awesome:

font-awesome:
  remote: https://fortawesome.github.io/Font-Awesome/
  version: 4.5.0
  license:
    name: MIT
    url: https://fortawesome.github.io/Font-Awesome/license/
    gpl-compatible: true
  css:
    theme:
      https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css: { type: external, minified: true }

Пример для Bootstrap CDN CSS с пользовательскими атрибутами.

bootstrap-cdn:
  remote: getbootstrap.com
  version: 4.0
  license:
    name: MIT
    url: https://github.com/twbs/bootstrap/blob/master/LICENSE
  css:
    theme:
      'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css':
        type: external
        minified: true
        attributes:
          crossorigin: anonymous
          integrity: "sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"

Больше информации