logo

Extra Block Types (EBT) - Neue Erfahrung im Layout Builder❗

Extra Block Types (EBT) - gestylte, anpassbare Blocktypen: Diashows, Registerkarten, Karten, Akkordeons und viele andere. Eingebaute Einstellungen für Hintergrund, DOM Box, Javascript Plugins. Erleben Sie die Zukunft der Layouterstellung schon heute.

Demo EBT-Module EBT-Module herunterladen

❗Extra Absatztypen (EPT) - Erfahrung mit neuen Absätzen

Extra Paragraph Types (EPT) - analoger, auf Absätzen basierender Satz von Modulen.

Demo EPT-Module EPT-Module herunterladen

Scroll

Hinzufügen von Stylesheets (CSS) und JavaScript (JS) zu einem Drupal 8 Modul

19/06/2025, by Ivan

Diese Dokumentation gilt für Module. Informationen zu Themes finden Sie im Abschnitt Hinzufügen von Stylesheets (CSS) und JavaScript (JS) in einem Drupal 8 Theme.

In Drupal 8 werden Stylesheets (CSS) und JavaScript (JS) über dasselbe System für Module (Code) und Themes geladen: Asset-Bibliotheken. Asset-Bibliotheken können eine oder mehrere CSS-Ressourcen, eine oder mehrere JS-Ressourcen und eine oder mehrere JS-Einstellungen enthalten.

Drupal folgt einem übergeordneten Prinzip: Ressourcen (CSS oder JS) werden nur geladen, wenn Sie Drupal mitteilen, dass sie geladen werden sollen. Drupal lädt nicht alle Ressourcen (CSS/JS) auf allen Seiten, da dies die Performance der Benutzeroberfläche beeinträchtigt.

Unterschiede zu Drupal 7

Es gibt zwei wichtige Unterschiede für Entwickler gegenüber Drupal 7:

1. Nur das JavaScript, das für eine bestimmte Seite benötigt wird, wird auf dieser Seite hinzugefügt. Insbesondere lädt Drupal standardmäßig kein JavaScript auf den meisten Seiten, die anonyme Benutzer sehen. Das bedeutet, dass jQuery nicht mehr automatisch auf allen Seiten geladen wird.
Wenn Ihr Theme also jQuery oder ein anderes JavaScript benötigt (das ebenfalls in einer Asset-Bibliothek definiert ist), müssen Sie Drupal mitteilen, dass dies der Fall ist, indem Sie eine Abhängigkeit zur entsprechenden Asset-Bibliothek deklarieren.

2. Das JavaScript-Objekt Drupal.settings wird durch drupalSettings ersetzt.

Prozess

Die Hauptschritte zum Laden von Ressourcen (CSS / JS):

1. Speichern Sie CSS oder JS in einer Datei.
2. Definieren Sie eine „Bibliothek“, die CSS- und JS-Dateien enthalten kann.
3. „Binden“ Sie die Bibliothek im Render-Array in einem Hook ein.

Für Themes gibt es eine Alternative zu Schritt 3: Themes können beliebig viele Asset-Bibliotheken auf allen Seiten laden.

Definition einer Bibliothek

Um eine oder mehrere Bibliotheken (Assets) zu definieren, fügen Sie im Stammverzeichnis Ihres Moduls eine Datei *.libraries.yml hinzu (zusammen mit der .info.yml Datei). Wenn Ihr Modul z.B. „fluffiness“ heißt, muss die Datei fluffiness.libraries.yml heißen. Jede „Bibliothek“ in der Datei ist ein Eintrag, der die CSS- und JS-Dateien (Assets) beschreibt, z.B.:
Ein besonderer Hinweis an französische Leser: Achten Sie darauf, .libraries.yml korrekt zu schreiben! Nicht .librairies.yml !! (Bibliothek heißt auf Französisch „librairie“ für Buchladen ...) Sonst werden Sie lange nach dem Fehler suchen, denn Drupal zeigt keinen Fehler beim Versuch an, eine nicht existente Bibliothek zu laden ... ;)

cuddly-slider:
  version: 1.x
  css:
    layout:
      css/cuddly-slider-layout.css: {}
    theme:
      css/cuddly-slider-theme.css: {}
  js:
    js/cuddly-slider.js: {}

Sie werden die Schlüssel 'layout' und 'theme' für CSS bemerken, die für JS nicht existieren. Diese geben den Stiltyp an, dem die CSS-Datei zugeordnet ist.

Sie können CSS-Gewichtung mit 5 verschiedenen Stilstufen setzen:

  • base: CSS Reset / Normalisierung plus Styling von HTML-Elementen. Schlüssel weist das Gewicht CSS_BASE = -200 zu
  • layout: das grobe Layout der Webseite, inklusive Grid-Systeme. Schlüssel weist das Gewicht CSS_LAYOUT = -100 zu
  • component: einzelne, wiederverwendbare UI-Elemente. Schlüssel weist das Gewicht CSS_COMPONENT = 0 zu
  • state: Stile, die mit Zustandsänderungen der Komponenten auf der Clientseite zusammenhängen. Schlüssel weist das Gewicht CSS_STATE = 100 zu
  • theme: rein visuelle Stile („Look & Feel“) für die Komponente. Schlüssel weist das Gewicht CSS_THEME = 200 zu

Das basiert auf dem SMACSS-Standard (SMACSS). Wenn Sie also 'theme' angeben, bedeutet das, dass die CSS-Datei themenbezogene Stile enthält, also reines Aussehen. Mehr Informationen hier. Sie können keine anderen Schlüssel verwenden, da dies zu strengen Warnungen führt.

Im obigen Beispiel wird angenommen, dass das eigentliche JavaScript cuddly-slider.js im Unterordner js Ihres Moduls liegt. Sie können JS auch von einer externen URL laden, CSS-Dateien einbinden und es gibt weitere Optionen. Siehe CDN / externe Bibliotheken für Details.

Beachten Sie, dass Drupal 8 jQuery standardmäßig nicht mehr auf allen Seiten lädt; Drupal 8 lädt nur, was benötigt wird. Deshalb müssen wir angeben, dass die Bibliothek cuddly-slider unseres Moduls eine Abhängigkeit auf die jQuery-Bibliothek deklariert. Diese wird nicht von einem Modul oder Theme bereitgestellt, sondern vom Drupal Core: core/jquery ist die Abhängigkeit, die wir deklarieren wollen. (Das ist der Formatname: Modulname / Bibliotheksname, also wenn eine andere Bibliothek von cuddly-slider abhängen will, muss sie fluffiness/cuddly-slider angeben, wobei fluffiness der Modulname ist.)

Also, um jQuery für js/cuddly-slider.js verfügbar zu machen, aktualisieren wir obiges Beispiel zu:

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

Der Reihenfolge nach werden die CSS- und JS-Ressourcen geladen, entsprechend der Auflistung.

Standardmäßig fügt Drupal JS-Ressourcen am Seitenende ein, um häufige Probleme zu vermeiden, wie das Blockieren des DOM-Ladevorgangs oder Zugriffe auf noch nicht existierende DOM-Elemente im jQuery-Code. Falls es notwendig ist, die JS-Assets im <head>-Bereich zu laden, kann die Option header genutzt werden, z.B.:

cuddly-slider:
  version: 1.x
  header: true
  js:
    js/cuddly-slider.js: {}

Dann wird js/cuddly-slider.js im Kopfbereich der Seite geladen.

Anfügen einer Bibliothek an Seiten

Je nachdem, welche Assets Sie laden müssen, können Sie die passende Asset-Bibliothek unterschiedlich anhängen. Manche Bibliotheken werden auf allen Seiten benötigt, andere selten, manche auf den meisten, aber nicht allen.

Wichtig ist, dass Sie nicht entscheiden, eine Bibliothek basierend auf der aktuellen Seite (URL oder Route) einzubinden, sondern basierend darauf, welche Elemente auf der Seite sichtbar sind: Wenn eine Seite z.B. '#type' => 'table', '#type' => 'dropbutton' und '#type' => 'foobar' enthält, laden wir nur die Bibliotheken, die mit diesen '#type's verbunden sind.
Sie sind aber nicht auf '#type' beschränkt: Sie können auch eine Bibliothek nur für eine bestimmte Instanz eines '#type' anhängen. Dann fügen Sie sie im Render-Array dieses Elements hinzu.

Natürlich gibt es nur selten einen guten Grund, eine Ressource auf allen Seiten zu laden (z.B. ein Analytics-JavaScript, das alle Seitenaufrufe trackt).

Anfügen an einen bestimmten „#type“ (für alle Instanzen)

Um eine Bibliothek an einen bestimmten vorhandenen '#type' für alle seine Instanzen anzuhängen, nutzen Sie hook_element_info_alter():

function yourmodule_element_info_alter(array &$types) {
  if (isset($types['table'])) {
    $types['table']['#attached']['library'][] = 'your_module/library_name';
  }
}

Danach Cache leeren, damit Drupal den neuen Hook erkennt.

Anfügen an ein Render-Array

Um eine Bibliothek an ein Render-Array (vielleicht an eine bestimmte Instanz eines '#type') anzuhängen, müssen Sie Zugriff auf dieses Render-Array haben. Sie definieren es möglicherweise selbst oder modifizieren es in einem Hook. Es sieht dann etwa so aus:

$build['the_element_that_needs_the_asset_library']['#attached']['library'][] = 'your_module/library_name';

Verwenden Sie immer numerische Schlüssel!
Sie könnten versucht sein, nicht numerische Schlüssel zu verwenden, um Duplikate zu vermeiden:

$build['the_element_that_needs_the_asset_library']['#attached']['library']['your_module/library_name'] = 'your_module/library_name';

Das sollten Sie nicht tun. Die Array-Merge-Logik in Drupal führt sonst zu ungültigen verschachtelten Arrays, was zu Fehlern wie diesen führt:

Warning: explode() expects parameter 2 to be string, array given in Drupal\Core\Asset\LibraryDependencyResolver->doGetDependencies()
Notice: Array to string conversion in system_js_settings_alter()
Notice: Array to string conversion in Drupal\Core\Asset\AttachedAssets->setLibraries()

Anfügen an das Render-Array eines Block-Plugins

Ein weiteres Beispiel: Wenn Sie ein Block-Plugin in Ihrem Modul definieren, können Sie in der build()-Methode der Klasse, die BlockBase erweitert, Bibliotheken an das Render-Array anhängen (seit Drupal 8 Beta 6):

    return [
      '#theme' => 'your_module_theme_id',
      '#someVariable' => $some_variable,
      '#attached' => [
        'library' => [
          'your_module/library_name',
        ],
      ],
    ];

Anfügen einer Bibliothek an ein Formular

Da Formulare Render-Arrays sind, funktioniert das Anfügen einer Bibliothek genauso:

/**
 * Implements hook_form_alter().
 */
function yourmodule_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  /* @var Drupal\Core\Entity\FieldableEntityInterface $entity */
  $formObject = $form_state->getFormObject();
  if ($formObject instanceof \Drupal\Core\Entity\EntityFormInterface) {
    $entity = $formObject->getEntity();
    if (
      $entity->getEntityTypeId() === 'node'
      && in_array($entity->bundle(), ['organisation', 'location', 'event', 'article'])
    ) {
      $form['#attached']['library'][] = 'yourmodule/yourlibrary';
    }
  }
}

Anfügen einer Bibliothek an alle (oder eine Teilmenge von) Seiten

Manchmal ist eine Asset-Bibliothek nicht an einen bestimmten Teil einer Seite gebunden, sondern an die gesamte Seite. Dafür gibt es hook_page_attachments(). Ein Beispiel findet sich im Modul „Contextual Links“:

// From core/modules/contextual/contextual.module.
function contextual_page_attachments(array &$page) {
  if (!\Drupal::currentUser()->hasPermission('access contextual links')) {
    return;
  }

  $page['#attached']['library'][] = 'contextual/drupal.contextual-links';
}

Anfügen einer Bibliothek in einer Preprocess-Funktion

Sie können eine Bibliothek auch in einer Preprocess-Funktion anhängen, indem Sie den speziellen Schlüssel '#attached' nutzen:

function yourmodule_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] =  'your_module/library_name';
}

Anfügen einer Bibliothek in einem Twig-Template

Sie können eine Bibliothek auch in einem Twig-Template mit der Twig-Funktion attach_library() anhängen. Beispiel in einem beliebigen *.html.twig:

{{ attach_library('your_module/library_name') }}
Some markup {{ message }}

Anfügen einer Bibliothek während eines Token-Replacements

Wenn Sie eigene Tokens definieren, können Sie eine Bibliothek anhängen, wenn Ihr Token im gefilterten Text vorkommt, indem Sie die Bibliothek zum BubbleableMetadata-Objekt in hook_tokens() hinzufügen:

/**
 * Implements hook_tokens().
 */
function your_module_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {
    $replacements = [];
    if ($type == 'your_module') {
        foreach ($tokens as $name => $original) {
            switch ($name) {
                case 'your-token':
                    $your_render_array = your_module_build_your_renderable_thing();
                    $replacements[$original] =  \Drupal::service('renderer')->render($your_render_array);
                    // HIER! Wir können auch Bibliotheken hinzufügen!
                    $bubbleable_metadata->addAttachments(['library' => ['your_module/library_name'] ]);
                break;
            }
        }
    }

    return $replacements;
}

Beachten Sie, dass in diesem Beispiel nur gezeigt wird, wie Sie eine Bibliothek beim Ersetzen anhängen. Um einen vollständigen benutzerdefinierten Token zu implementieren, müssen Sie auch hook_token_info() implementieren.

Anfügen einer Bibliothek in einem Filter-Plugin

Wenn Ihr Modul einen Textfilter bereitstellt, kann es die Methoden setAttachments() oder addAttachments() der Klasse FilterProcessResult verwenden. Zum Beispiel macht der Filter filter_caption das so:

    if (...) { ...
      $result->setProcessedText(Html::serialize($dom))
        ->addAttachments([
          'library' => [
            'filter/caption',
          ],
        ]);
    }

    return $result;

Einbinden von benutzerdefiniertem JavaScript

Manchmal wollen Sie JavaScript auf der Seite hinzufügen, das von einigen PHP-berechneten Informationen abhängt. Sie können dafür drupalSettings verwenden (der Nachfolger von Drupal 7s Drupal.settings), ein Array von Einstellungen, das in Ihrem PHP-Code definiert wird und in JavaScript als Einstellungsobjekt verfügbar ist.

Um drupalSettings in einer Bibliothek zu nutzen, müssen Sie zuerst die Abhängigkeit core/drupalSettings in der Bibliotheksdefinition deklarieren.

Also würde die Bibliotheksdefinition aus unserem vorherigen Beispiel so aussehen:

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

In unseren PHP-Dateien können wir jetzt die nötigen drupalSettings zusammen mit der Bibliothek anhängen. Üblicherweise verwenden wir den Modulnamen in lowerCamelCase als Schlüssel für die Einstellungen und den Bibliotheksnamen ebenfalls in lowerCamelCase als weiteren Schlüssel.

Wenn wir z.B. berechnete Werte 'foo' und 'baz' von PHP an unser JavaScript übergeben wollen, könnten wir Folgendes machen:

$computed_settings = [
  'foo' => 'bar',
  'baz' => 'qux',
];

$build['#attached']['library'][] = 'your_module/library_name';
$build['#attached']['drupalSettings']['fluffiness']['cuddlySlider'] = $computed_settings;

Dann kann cuddly-slider.js auf drupalSettings.fluffiness.cuddlySlider.foo und drupalSettings.fluffiness.cuddlySlider.baz zugreifen, die die Werte „bar“ und „qux“ enthalten.

Render-Arrays werden zwischengespeichert. Abhängig von der Natur Ihrer berechneten Werte und des Elements, dem Sie drupalSettings hinzufügen, müssen Sie möglicherweise die Cache-Metadaten entsprechend anpassen.

Hinzufügen von Attributen zu Skriptelementen

Wenn Sie Attribute zum Script-Tag hinzufügen möchten, müssen Sie den Schlüssel attributes im JSON nach der Skript-URL hinzufügen. Innerhalb des Objekts hinter dem attributes-Schlüssel fügen Sie den Namen des Attributs als neuen Schlüssel hinzu. Der Wert dieses Schlüssels ist der Wert des Attributs. Wenn dieser Wert true ist, wird das Attribut ohne Wert ausgegeben.

Beispiel:

 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 } }

Dies erzeugt das folgende Markup:

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

Deaktivieren der Aggregation

Standardmäßig werden mehrere lokale Dateien zusammengeführt, wo möglich. Um das für eine Datei zu deaktivieren, setzen Sie deren Flag preprocess auf false.

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

CDN / externe Bibliotheken

Manchmal wollen Sie JavaScript von einem CDN (Content Delivery Network) nutzen, um die Seitenladegeschwindigkeit zu verbessern. Dies kann durch Deklarieren einer „externen“ Bibliothek erfolgen. Es ist auch hilfreich, einige Metadaten über die externe Bibliothek anzugeben.

angular.angularjs:
  remote: https://github.com/angular/angular.js
  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 }

Inline-JavaScript

Inline-JavaScript wird stark abgeraten. Es wird empfohlen, stattdessen JavaScript in eine Datei auszulagern, da dies clientseitiges Caching ermöglicht und den Code besser lesbar macht. Inline-JavaScript kollidiert außerdem mit Content-Security-Policy vieler Seiten und macht Ihr Modul für diese unbrauchbar.

Inline-JavaScript, das Markup generiert

Das wird nicht empfohlen. Packen Sie JavaScript stattdessen in eine Datei. Beispiele hierfür sind Werbung, Social-Media-Share-Buttons und Social-Media-Widget-Listen, die Inline-JavaScript verwenden. Dies ist einfach eine besondere Art von Inhalt/Markup, da es nicht zur Gestaltung oder Interaktivität der Seite dient, sondern externen Inhalt über JavaScript lädt.

Sie können sie in einem benutzerdefinierten Block oder direkt im Twig-Template platzieren.

Beispiel:

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

Inline-JavaScript, das die gesamte Seite betrifft

Inline-JavaScript wird stark abgeraten. Beispiele hierfür sind Analytik (z.B. Google Analytics) und gehostete Schriftarten. Inline-JavaScript, das die gesamte Seite betrifft, gehört zu zwei Kategorien: Frontend/Styling oder Logik. Die meisten Fälle lassen sich mit festem JavaScript in einer Datei plus zusätzlicher Konfiguration abdecken.

Bei Frontend/Styling (z.B. gehostete Schriftarten) gehört es zum Theme, siehe dazu „Hinzufügen von Stylesheets (CSS) und JavaScript (JS) in einem Drupal 8 Theme“.

Bei anderen Fällen gehört das JavaScript zum Modul. Im entsprechenden Hook — meistens hook_page_attachments() — definieren Sie eingebettete HTML-HEAD-Daten über den Schlüssel 'html_head' im #attached-Array:

function fluffiness_page_attachments(array &$attachments) {
  $attachments['#attached']['html_head'][] = [
    // Die Daten.
    [
      '#type' => 'html_tag',
      // Das HTML-Tag, hier ein  tag.
      '#tag' => 'script',
      // Der Inhalt des Tags, z.B. alert("Hello world!");
      '#value' => 'alert("Hello world!");',
      // Attribute wie src zum Laden einer Datei.
      '#attributes' => ['src' => ''],

    ],
    // Ein Schlüssel, um das HTML-Element bei Änderungen zu identifizieren.
    'hello-world',
  ];
}

Dynamisch generiertes CSS und JS

In sehr seltenen und komplexen Fällen müssen Sie möglicherweise dynamisch CSS und JS generieren. Es gibt zwei Kategorien von „Dynamik“:

        1. Dynamisch erzeugt, aber für mehrere Anfragen wiederverwendbar
        2. Dynamisch für jede Anfrage erzeugt

Wenn dynamisches CSS/JS für mehrere Anfragen verwendet wird, können Sie hook_library_info_alter() nutzen, um die Bibliothek zu ändern und Ihr dynamisch erzeugtes CSS/JS einzubinden. Ein Beispiel im Drupal Core ist color_library_info_alter(). Beachten Sie, dass das bloße Hinzufügen einer Bibliothek per hook_library_info_build() oder hook_library_info_alter() nicht automatisch dafür sorgt, dass die Bibliothek auf der Seite erscheint. Sie müssen sie weiterhin per Anhang (auf Seite oder Element) einbinden, wie oben beschrieben.

Wenn dynamisches CSS/JS für jede Anfrage erzeugt wird, bewegen Sie sich in einem wirklich fortgeschrittenen Bereich. Das ist kompliziert und aus gutem Grund: dynamische Assets müssen bei jeder Anfrage neu erstellt werden, was Drupal verlangsamt. Wir wollen diese Verlangsamung erschweren und bieten daher keine gute API dafür an, weil wir nicht wollen, dass Sie das tun.

Es ist dennoch möglich. Im Fall von dynamischem JS: Überlegen Sie stattdessen, benutzerdefiniertes JavaScript zu verwenden, das fast immer die bessere Wahl ist. Die Logik wird dann in einer Datei gespeichert (kann angesehen, debuggt und clientseitig gecacht werden), und bei jeder Anfrage müssen nur noch Konfigurationswerte erzeugt werden, die an diese Logik übergeben werden. Das funktioniert auch für dynamisches CSS: binden Sie das dynamische CSS als drupalSettings ein und lassen Sie ein JS-File es zur Laufzeit auf die Seite bringen.

Wenn drupalSettings + JS-File keine Option sind, bleibt Ihnen noch die Möglichkeit, hook_page_attachments() zu verwenden, wo Sie dem $page['#attached']['html_head'] ein

Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.