logo

Types de blocs supplémentaires (EBT) – Nouvelle expérience de Layout Builder❗

Types de blocs supplémentaires (EBT) – types de blocs stylisés et personnalisables : diaporamas, onglets, cartes, accordéons et bien d’autres. Paramètres intégrés pour l’arrière-plan, la boîte DOM, les plugins JavaScript. Découvrez dès aujourd’hui le futur de la création de mises en page.

Démo des modules EBT Télécharger les modules EBT

❗Types de paragraphes supplémentaires (EPT) – Nouvelle expérience Paragraphes

Types de paragraphes supplémentaires (EPT) – ensemble de modules basé sur les paragraphes analogiques.

Démo des modules EPT Télécharger les modules EPT

Défilement

Ajout de feuilles de style (CSS) et de JavaScript (JS) dans un module Drupal 8

05/07/2025, by Ivan

Menu

Cette documentation est destinée aux modules. Pour les thèmes, consultez la section Ajout de feuilles de style (CSS) et JavaScript (JS) dans un thème Drupal 8.

Dans Drupal 8, les feuilles de style (CSS) et JavaScript (JS) sont chargés via un système unique pour les modules (code) et les thèmes : la bibliothèque de ressources. Une bibliothèque d'actifs peut contenir un ou plusieurs fichiers CSS, un ou plusieurs fichiers JS, et une ou plusieurs configurations JS.

Drupal utilise un principe de haut niveau : les ressources (CSS ou JS) ne sont chargées que si vous indiquez à Drupal qu'elles doivent l'être. Drupal ne charge pas toutes les ressources (CSS/JS) sur toutes les pages, car cela nuirait aux performances de l'interface.

Différences par rapport à Drupal 7

Il y a deux différences importantes pour les développeurs par rapport à Drupal 7 :

1. Seul le JavaScript nécessaire à une page spécifique sera ajouté à cette page. En particulier, par défaut, Drupal n'a pas besoin de JavaScript sur la plupart des pages visibles par les utilisateurs anonymes. Cela signifie que jQuery n'est plus chargé automatiquement sur toutes les pages.
Donc, si votre thème nécessite jQuery ou un autre JavaScript (qui est aussi défini dans une bibliothèque de ressources), vous devez informer Drupal en déclarant la dépendance à la bibliothèque d'actifs nécessaire.

2. L'objet JavaScript Drupal.settings est remplacé par drupalSettings.

Processus

Les étapes principales pour charger des ressources (CSS / JS) :

1. Enregistrez les fichiers CSS ou JS.
2. Définissez une « bibliothèque » pouvant contenir des fichiers CSS et JS.
3. « Attachez » la bibliothèque au tableau de rendu dans un hook.

Pour les thèmes, il existe une alternative à l'étape 3 : les thèmes peuvent charger n'importe quel nombre de bibliothèques de ressources sur toutes les pages.

Définition d'une bibliothèque

Pour définir une ou plusieurs bibliothèques (ressources), ajoutez un fichier *.libraries.yml à la racine du dossier de votre module (avec le fichier .info.yml). (Si votre module s'appelle fluffiness, le fichier doit être fluffiness.libraries.yml). Chaque « bibliothèque » dans le fichier est une entrée détaillant les fichiers CSS et JS, par exemple :
Note spéciale pour les francophones : faites attention à bien écrire .libraries.yml ! Pas .librairies.yml !! (une librairie en français désigne une bibliothèque de livres...) sinon vous pouvez chercher longtemps la source du problème car Drupal ne génère pas d'erreur quand on essaie d'attacher une bibliothèque inexistante ... ;)

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

Vous remarquerez les clés 'layout' et 'theme' pour le CSS, absentes pour le JS. Elles indiquent le type de style du fichier CSS.

Vous pouvez définir le poids CSS selon 5 niveaux différents :

  • base : reset / normalisation CSS plus styles des éléments HTML. Clé attribuant le poids CSS_BASE = -200
  • layout : macro-structure de la page web, incluant les systèmes de grille. Clé attribuant le poids CSS_LAYOUT = -100
  • component : éléments discrets réutilisables de l’interface utilisateur. Clé attribuant le poids CSS_COMPONENT = 0
  • state : styles liés aux changements des composants côté client. Clé attribuant le poids CSS_STATE = 100
  • theme : style purement visuel (« apparence ») du composant. Clé attribuant le poids CSS_THEME = 200

Cela est défini par la norme SMACSS. Donc, si vous indiquez theme, cela signifie que le fichier CSS contient les styles liés au thème, purement visuels. Plus d’informations ici. Vous ne pouvez pas utiliser d’autres clés, car elles provoqueraient des avertissements stricts.

Dans cet exemple, on suppose que le JavaScript cuddly-slider.js est dans un sous-dossier js de votre module. Vous pouvez aussi référencer un JS externe, inclure des fichiers CSS, et d’autres options. Voir CDN / bibliothèques externes pour plus de détails.

Notez cependant que Drupal 8 ne charge plus jQuery sur toutes les pages par défaut ; il ne charge que ce qui est nécessaire. Nous devons donc déclarer que la bibliothèque cuddly-slider de notre module dépend de la bibliothèque contenant jQuery. Ce n’est pas un module ni un thème qui fournit jQuery, mais le cœur Drupal : core/jquery est la dépendance que nous voulons déclarer. (C’est un nom d’extension suivi d’un slash puis du nom de la bibliothèque ; donc si une autre bibliothèque veut dépendre de cuddly-slider, elle devra déclarer la dépendance fluffiness/cuddly-slider, fluffiness étant le nom de notre module.)

Ainsi, pour rendre jQuery disponible à js/cuddly-slider.js, nous mettons à jour comme suit :

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

Comme attendu, l’ordre d’énumération des ressources CSS et JS est aussi leur ordre de chargement.

Par défaut, Drupal attache les ressources JS en bas de page pour éviter certains problèmes fréquents, comme : blocage du chargement du DOM, accès à un élément DOM non prêt depuis du code jQuery, etc. Si vous devez absolument charger les ressources JS dans la balise <head>, utilisez l’option header, ainsi :

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

Le fichier js/cuddly-slider.js sera alors attaché en haut de la page.

Attacher une bibliothèque aux pages

Selon les actifs à charger, vous pouvez attacher la bibliothèque correspondante de différentes façons. Certaines bibliothèques sont nécessaires sur toutes les pages, d’autres très rarement, d’autres sur la plupart mais pas toutes.

Mais ce qui importe surtout, c’est que nous ne décidons pas d’attacher une bibliothèque en fonction de la page (URL ou route), mais en fonction des éléments visibles sur la page : si la page contient '#type' => 'table', '#type' => 'dropbutton' et '#type' => 'foobar', alors on ne chargera que les bibliothèques liées à ces types #.
Mais on n’est pas limité aux seuls '#type' : on peut vouloir charger une bibliothèque spécifique seulement pour une instance donnée de '#type'. Dans ce cas, on l’attache au tableau de rendu de cette instance.

Évidemment, il est rare d’avoir une raison solide pour charger un actif sur toutes les pages (par exemple un JavaScript d’analyse qui trace les visites), indépendamment des éléments présents.

Les sections suivantes donnent des exemples pour chacune de ces méthodes.

Attacher à un certain « #type » (pour toutes ses instances)

Pour attacher une bibliothèque à un « #type » existant pour toutes ses instances, utilisez hook_element_info_alter() :

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

Pensez ensuite à vider le cache pour que Drupal prenne en compte ce nouveau hook.

Attacher au tableau de rendu

Pour attacher une bibliothèque au tableau de rendu (et peut-être à une instance spécifique d’un certain '#type'), vous devez avoir accès à ce tableau de rendu. Vous pouvez définir ce tableau vous-même ou le modifier dans un hook. Par exemple :

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

Utilisez toujours des clés numériques !
Vous pourriez vouloir aider Drupal à ne pas créer de doublons en utilisant des clés non numériques :

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

Ne faites pas cela. La fusion des tableaux en Drupal produira un tableau imbriqué invalide, et vous obtiendrez des erreurs comme (googlefood) :

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

Attacher au tableau de rendu d’un plugin de bloc

Voici un autre exemple : si vous construisez un plugin de bloc dans votre module, vous pouvez attacher des bibliothèques dans la fonction build() de votre classe étendant BlockBase (depuis Drupal 8 beta 6) :

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

Attacher une bibliothèque à un formulaire

Les formulaires étant simplement des tableaux de rendu, l’attachement d’une bibliothèque fonctionne de la même manière :

/**
 * Implémente 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';
    }
  }
}

Attacher une bibliothèque à toutes (ou certaines) pages

Parfois, une bibliothèque d’actifs ne correspond pas à une partie spécifique de la page, mais à la page entière. Pour cela, il existe hook_page_attachments(). Un exemple célèbre se trouve dans le module Contextual Links :

// Depuis 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';
}

Attacher une bibliothèque dans une fonction de prétraitement

Vous pouvez attacher une bibliothèque dans une fonction de prétraitement en utilisant la clé spéciale '#attached' :

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

Attacher une bibliothèque dans un template Twig

Vous pouvez aussi attacher une bibliothèque dans un template Twig, en utilisant la fonction attach_library(). Par exemple, dans un fichier *.html.twig :

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

Attacher une bibliothèque lors du remplacement d’un token

Vous pouvez attacher une bibliothèque si votre propre token est présent dans un texte filtré, en ajoutant la bibliothèque à l’objet BubbleableMetadata lors du remplacement dans hook_tokens() :

/**
 * Implémente 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);
                    // REGARDEZ ICI ! NOUS POUVONS AUSSI AJOUTER DES BIBLIOTHÈQUES !
                    $bubbleable_metadata->addAttachments(['library' => ['your_module/library_name'] ]);
                break;
            }
        }
    }

    return $replacements;
}

Notez que cet exemple montre seulement comment attacher une bibliothèque pendant le remplacement - pour implémenter complètement un token personnalisé, vous devez aussi implémenter hook_token_info().

Attacher une bibliothèque dans un plugin de filtre

Si un module fournit un filtre de texte, il peut utiliser les méthodes setAttachments() ou addAttachments() de la classe FilterProcessResult. Par exemple, le filtre filter_caption fait cela :

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

    return $result;

Ajouter du JavaScript personnalisé

Parfois, vous voulez ajouter du JavaScript sur une page qui dépend d’informations calculées en PHP. Vous pouvez utiliser drupalSettings (successeur de Drupal 7 Drupal.settings), un tableau de configurations défini dans votre script PHP, accessible comme un objet de configuration dans votre JavaScript.

Pour utiliser drupalSettings dans une bibliothèque, vous devez d’abord déclarer la dépendance à core/drupalSettings dans la définition de la bibliothèque.

Ainsi, la définition de la bibliothèque de notre exemple précédent :

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

Dans nos fichiers PHP, nous pouvons maintenant transmettre les drupalSettings nécessaires avec notre bibliothèque. Par convention, nous utilisons le nom du module en lowerCamelCase comme clé pour les réglages et le nom de la bibliothèque en lowerCamelCase comme clé secondaire.

Si nous voulons transmettre les valeurs calculées 'foo' et 'baz' de PHP vers le JavaScript de notre exemple, on peut faire :

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

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

Alors cuddly-slider.js pourra accéder à drupalSettings.fluffiness.cuddlySlider.foo et drupalSettings.fluffiness.cuddlySlider.baz, avec les valeurs « bar » et « qux » respectivement.

Les tableaux de rendu sont mis en cache. Selon la nature de vos valeurs calculées et du composant auquel vous attachez drupalSettings, vous devrez peut-être ajuster les métadonnées de mise en cache en conséquence.

Ajouter des attributs aux éléments script

Si vous voulez ajouter des attributs à la balise script, vous devez ajouter une clé d’attributs dans le JSON après l’URL du script. Dans l’objet suivant la clé attributs, ajoutez le nom de l’attribut que vous voulez afficher dans la balise script comme nouvelle clé. La valeur associée sera la valeur de l’attribut. Si cette valeur est true, l’attribut sera affiché seul sans valeur.

Par exemple :

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

Cela générera le balisage :

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

Désactivation de l’agrégation

Par défaut, plusieurs fichiers locaux sont agrégés lorsque c’est possible. Pour désactiver cela pour un fichier, définissez son drapeau 'preprocess' à false.

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

CDN / bibliothèques externes

Vous pouvez vouloir utiliser du JavaScript hébergé sur un CDN (Content Delivery Network) pour accélérer le chargement de la page. Cela se fait en déclarant une bibliothèque « distante ». Il est aussi utile d’inclure des informations sur la bibliothèque externe dans la définition.

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 }

JavaScript inline

Le JavaScript inline est fortement déconseillé. Il est préférable de placer le JavaScript que vous voulez inline dans un fichier JS, car cela permet la mise en cache côté client. Cela permet aussi que le code JS soit lisible et modifiable. Le JS inline entre aussi en conflit avec la politique de sécurité de contenu de nombreux sites et rend votre module inutilisable pour eux.

JavaScript inline qui génère du balisage

Cela n’est pas recommandé. Mettez plutôt le JavaScript dans un fichier. Des exemples incluent la publicité, les boutons de partage sur les réseaux sociaux, les widgets de listes sociales. Ils utilisent du JavaScript inline, mais c’est un type particulier de contenu/balisage, car ils ne sont pas destinés à décorer ou rendre interactif le contenu du site, mais à extraire du contenu externe via JavaScript.

Vous voudrez placer cela dans un bloc personnalisé ou même directement dans un template Twig.

Par exemple :

<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 inline affectant toute la page

Le JavaScript inline est fortement déconseillé. Des exemples de JavaScript inline qui affecte toute la page sont l’analytique (par exemple Google Analytics) et les services de polices hébergées. Le JS inline affectant toute la page peut appartenir à deux catégories : front-end / style ou logique. La plupart des cas peuvent être couverts par un JS fixe dans un fichier plus des réglages complémentaires.

Dans le cas d’une interface/style externe (par exemple un service de polices hébergées), cela concerne un thème, consultez donc « Ajout de feuilles de style (CSS) et JavaScript (JS) dans un thème Drupal 8 ».

Dans l’autre cas, le JS appartient à un module. Dans le hook approprié — souvent hook_page_attachments() — vous définissez les données HTML pour la balise <HEAD> avec la clé 'html_head' dans #attached :

function fluffiness_page_attachments(array &$attachments) {
  $attachments['#attached']['html_head'][] = [
    // Les données.
    [
      '#type' => 'html_tag',
      // La balise HTML à ajouter, ici une balise script.
      '#tag' => 'script',
      // Le contenu de la balise HTML, ici on veut avoir
      // alert("Hello world!");.
      '#value' => 'alert("Hello world!");',
      // Définir des attributs comme src pour charger un fichier.
      '#attributes' => array('src' => ''),

    ],
    // Une clé pour reconnaître cet élément HTML lors d’altérations.
    'hello-world',
  ];
}

CSS et JS générés dynamiquement

Dans des cas très rares et complexes, vous pourriez avoir besoin de générer dynamiquement CSS et JS. Il y a deux types de « dynamisme » :

                1. Généré dynamiquement mais utilisé pour plusieurs requêtes
                2. Généré dynamiquement pour chaque requête

Si un CSS / JS dynamique est utilisé sur plusieurs requêtes, vous pouvez utiliser hook_library_info_alter() pour modifier la bibliothèque afin d’inclure votre CSS / JS généré dynamiquement ou automatiquement. Un exemple dans le cœur Drupal 8 est color_library_info_alter(). Comprenez que juste utiliser hook_library_info_build() ou hook_library_info_alter() pour ajouter une bibliothèque ne la fera pas automatiquement apparaître sur la page. Vous devez toujours l’attacher à la page ou à un élément, avec l’une des méthodes décrites ci-dessus.

Si le CSS / JS dynamique est généré pour chaque requête, vous entrez dans un domaine vraiment avancé. C’est compliqué, et pour une bonne raison : les actifs dynamiques pour chaque requête doivent être reconstruits à chaque requête, ce qui ralentit Drupal. Nous voulons rendre ce ralentissement difficile, donc nous ne fournissons pas d’API simple pour cela car nous ne voulons pas que vous le fassiez.

Cependant, c’est possible. Pour le JS dynamique : envisagez plutôt d’utiliser du JavaScript personnalisé ; c’est presque toujours un meilleur choix. Ensuite, la logique est dans un fichier (qui peut être vu, débogué et mis en cache côté client), et à chaque requête, vous ne créez que les paramètres de configuration dans ce fichier. Cela peut aussi servir pour le CSS dynamique : attachez le CSS dynamique via drupalSettings et laissez un fichier JS l’ajouter à la page.

Si drupalSettings + un fichier JS ne sont pas une option, il vous reste une possibilité : utilisez hook_page_attachments(), où vous ajoutez une nouvelle entrée dans $page['#attached']['html_head'], contenant une balise <script> ou <style>, comme montré dans la section « JavaScript inline affectant toute la page » ci-dessus.

Ajout de hook_library_info_build() pour définir des bibliothèques dynamiques

Pour certains cas d’utilisation avancés — comme détecter des bibliothèques tierces à charger manuellement puis les présenter comme des bibliothèques Drupal — vous voulez pouvoir utiliser du code PHP pour enregistrer des bibliothèques avec une logique supplémentaire. C’est pourquoi hook_library_info_build() a été ajouté.

Notez que « dynamique » ne signifie pas « au runtime » (à chaque requête) — ce serait catastrophique pour les performances. Les bibliothèques ajoutées dynamiquement sont toujours mises en cache comme celles définies dans les fichiers YML. Vous devez toujours attacher la bibliothèque à une page ou un élément avec une des méthodes ci-dessus. C’est « dynamique » parce que vous pouvez utiliser de la logique pour contrôler l’enregistrement des bibliothèques.

Différences par rapport à Drupal 7

Plus d’informations