logo

パレット - カラフルに🎨

Palette — ビジュアルページビルダー、デザインの専門知識は不要です。

ライブデモ パレットをダウンロード

Scroll

Drupal 8テーマへのスタイルシート(CSS)とJavaScript(JS)の追加

03/05/2020, by maria

このドキュメントはテーマ用です。モジュールについてはDrupal 8モジュールへのスタイルシート(CSS)とJavaScript(JS)の追加のセクションを参照してください。

Drupal 8では、スタイルシート(CSS)とJavaScript(JS)は、モジュール(コード)とテーマの両方で同じシステムを介して読み込まれます:アセットライブラリです。

明確にするために、これらの手順はテーマ専用であり、モジュールには適用されません。

Drupalは高レベルの原則を使用します:アセット(CSSまたはJS)は、Drupalに読み込む必要があると通知した場合にのみ読み込まれます。Drupalはフロントエンドのパフォーマンスを低下させるため、すべてのページでアセットを読み込みません。

Drupal 7との違い

テーマ設定に関してDrupal 7と比較して6つの重要な違いがあります:

  • THEME.info.ymlファイルがTHEME.infoファイル(同じデータ)に取って代わりました。
  • THEME.infoのstylesheetsプロパティ(CSS追加用)は削除され、`*`がテーマまたはモジュールの名前である*.libraries.ymlに置き換えられました。
  • THEME.infoのscriptsプロパティ(JS追加用)も削除され、`*`がテーマまたはモジュールの名前である*.libraries.ymlに置き換えられました。
  • ページで必要なCSS、JSのみが読み込まれます。たとえば、jQueryは*.libraries.ymlで明示的に指定されない限り、自動的に読み込まれなくなりました。テーマがjQueryや、すべてのページで読み込みたい他のアセットを必要とする場合は、*.libraries.ymlに追加します。次に、ライブラリをTHEME.info.ymlに含めます。
  • Drupal 7では、ライブラリはhook_library_info()を使用して定義する必要がありました。これは*.libraries.ymlファイルに置き換えられました。
  • Drupal 8では、drupal_add_css()、drupal_add_js()、drupal_add_library()が#attachedに置き換えられて削除されました

 

プロセス

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_themeで宣言されたmy_library、my_moduleで宣言されたmy_libraryに依存する場合、依存関係を次のように宣言する必要があります:

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

モジュール名とテーマ名は、同じ名前のライブラリに名前空間を提供します。

すべてのページへのライブラリのアタッチ

ほとんどのテーマは、テーマがアクティブなすべてのページで読み込む必要があるスタイルシート(CSSファイル)にglobal-stylingアセットライブラリを使用します。global-scriptsアセットライブラリを介してJSでも同じことが可能です

# 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: {}   

テーマ全体で利用できるようにするには、global-styling/global-scriptsライブラリをテーマのinfo.yml(この場合は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テンプレートを介したライブラリのアタッチ

任意の*.html.twigファイルでattach_library()関数を使用して、アセットライブラリをTwigテンプレートにアタッチできます。例:

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

ページのサブセットへのライブラリのアタッチ

場合によっては、ライブラリをすべてのページで有効にするのではなく、ページのサブセットでのみ有効にしたい場合があります。たとえば、特定のブロックが表示されているとき、または特定のノードタイプが表示されているときにのみライブラリを有効にしたい場合があります。

テーマは、.themeファイルにTHEME_preprocess_HOOK()関数を実装することでこれを実行できます。「THEME」をテーマのマシン名に、「HOOK」をテーマフックのマシン名に置き換えます。

たとえば、メンテナンスページにJavaScriptをアタッチする場合、「HOOK」部分は「maintenance_page」で、関数は次のようになります:

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

他のテーマフックに対しても同様のことができます。もちろん、関数にロジックを含めることもできます。たとえば、「block」フックでどのブロックが前処理されているか、「node」フックでどのノードタイプかを判断するなどです。

重要な注意! この場合、状態に一致するキャッシュメタデータを指定する必要があります!上記の例は無条件に機能するため、キャッシュメタデータは必要ありません。最も一般的なユースケースは、現在のルートに基づいてアセットライブラリをアタッチする場合です:

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 アセットを集約する必要があるかどうか。
デフォルト:true
{ preprocess: false }

 

type アセットのソース。
デフォルト:file
{ 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 アセットを集約する必要があるかどうか。
デフォルト:true
{ preprocess: false }

 

type アセットのソース。
デフォルト:file
{ type: external, minified: true }

 

weight 代わりに依存関係を使用することをお勧めします。
他のアセットに対する順序を調整します。負の値である必要があります。
{ weight: -1 }

 

 

ライブラリのオーバーライドと拡張

*.libraries.ymlで定義されたライブラリをオーバーライドするには、*.info.ymlに移動する必要があります。それらは、libraries-overrideまたはlibraries-extendを使用してオーバーライドまたは拡張できます。*.info.ymlに追加するオーバーライドは、サブテーマに継承されます。

*.info.ymlファイルで使用されるstylesheets-removeプロパティは廃止され、Drupal 9.0.xで削除される予定です。stylesheets-overrideプロパティはすでに削除されています。

libraries-override

オーバーライドを作成するときに使用するロジック:

  • ライブラリ名には、元のモジュール(またはコア)の名前空間を使用します。
  • キーとして、最新のオーバーライドへのパスを使用します。
  • このパスはファイルへの完全なパスである必要があります。

例:

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

ここで、contextual/drupal.contextual-linksは基本ライブラリの名前空間で、/core/themes/stable/css/contextual/contextual.module.cssはこのライブラリの最新のオーバーライドへの完全なパスです。この場合、ファイルはfalseでオーバーライドされています。

ここで重要なのは、最後の部分だけが実際のファイルシステムパスであり、残りは名前空間を参照していることです。css:行とcomponent:行は、オーバーライドされるライブラリの構造を反映しています。

これを使用する場合、ファイルシステムパスへの依存は、サイトのファイル構造が変わるとパスが壊れる可能性があることを意味することを忘れないでください。このため、ストリームラッパーを使用して完全なパスへの依存を削除する問題があります。

テーマがモジュールやテーマから継承したCSSまたはJavaScriptアセット、またはライブラリ全体を削除または置換するためのlibraries-overrideの他の使用方法をいくつか示します。

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

libraries-extendを使用すると、テーマはライブラリがアタッチされるたびにテーマ固有の追加のライブラリアセットを追加して、ライブラリアセットを変更できます。
libraries-extendは、ライブラリを他の任意の数のライブラリで拡張することによって定義されます。

これは、グローバルCSSでそれを行うことなく、テーマ内のいくつかのコンポーネントを異なる方法でスタイル設定するのに理想的です。つまり、すべてのページでそれを行うためのCSSを読み込むことなく、コンポーネントの外観を調整します。

# Extend drupal.user: add assets from classy's user libraries.
libraries-extend:
  core/drupal.user: 
    - classy/user1
    - classy/user2

追加のJavaScript設定

アセットの読み込み順序

予想どおり、ファイルがリストされる順序が読み込み順序です。デフォルトでは、すべてのJSアセットがフッターに読み込まれます。対応するJSが実行されないと表示できない重要なUI要素のJSは、必要に応じて次のようにヘッダーに読み込むことができます:

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

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

このアセットライブラリのJavaScriptアセットが「クリティカルパス」上にあり、ヘッダーから読み込む必要があることを示すには、headerプロパティをtrueに設定します。このように宣言されたライブラリの直接または間接の依存関係も自動的にヘッダーから読み込まれるため、利用できるように個別に宣言する必要はありません。これが「クリティカルパス」というフレーズの意味です。アセットがヘッダーで宣言されると、そのアセットとそのすべての依存関係を最初に読み込むことが「クリティカル」になります。

カスタムJavaScriptのアタッチ:

場合によっては、計算されたPHP情報に依存するJavaScriptをページに追加したい場合があります。

この場合、JavaScriptファイルを作成し、以前と同じようにライブラリを定義してアタッチしますが、JavaScript設定もアタッチし、そのJavaScriptファイルにdrupalSettings(Drupal 7のDrupal.settingsの後継)を介してこれらの設定を読み取らせます。ただし、drupalSettingsをJavaScriptファイルで利用できるようにするには、jQueryを利用できるようにするのと同じ作業を行う必要があります。drupalSettingsへの依存関係を宣言する必要があります。

したがって、次のようになります:

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

スクリプト要素への属性の追加

scriptタグに属性を追加する場合は、スクリプトのURLの後にあるJSONにattributesキーを追加する必要があります。attributesキーの後に続くオブジェクト内に、新しいキーとしてスクリプトに表示する属性名を追加します。このキーの値が属性値になります。この値が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ファイルに配置することをお勧めします。これにより、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は、フロントエンド/スタイリングまたはロジックの2つのカテゴリのいずれかに分類できます。

フロントエンド/スタイリングの場合(ホスト型フォントサービスなど)、JSはテーマに属します。JSをhtml.html.twigファイルに直接配置してください。フォントの場合、これにより、フォントがまだ読み込み中にFOUT(Flash Of Unstyled Text)を防ぐことができるため、エンドユーザーにとって最高(そして最速)のフロントエンドが得られる場所に適切に配置できます(JSを介して読み込まれるフォントは、CSSの前にHTML <HEAD>にリストする必要があります)!
(詳細については、優れた記事「Async Typekit & Micro-FOUT」を参照してください。)

もう1つのケースでは、モジュールに属します。それについては、「Drupal 8モジュールへのスタイルシート(CSS)とJavaScript(JS)の追加」を参照してください。

統合モジュールのインラインJavaScript

インラインJavaScriptの使用は強く推奨されません。上記の例のいずれかを使用できる場合は、これを試す前にそれらを検討してください。

サイトユーザーが提供するインラインJavaScriptを受け入れるフィールドを提供する際に考慮すべき2つの点:

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 / 外部ライブラリ

CDN(コンテンツ配信ネットワーク)にある外部のJavaScriptを使用したい場合があります。たとえば、Webフォントは通常、外部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"

詳細情報