Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
This documentation is for modules. For the themes, see Adding stylesheets (CSS) and
JavaScript (JS) to a Drupal 8 theme.
In Drupal 8, stylesheets (CSS) and JavaScript (JS) are loaded through the same system for
modules (code) and themes, for everything: asset libraries. Asset libraries can contain one or
more CSS assets, one or more JS assets and one or more JS settings.
Drupal uses a high-level principle: assets (CSS or JS) are still only loaded if you tell
Drupal it should load them. Drupal does not load all assets (CSS/JS) on all
pages because this is bad for front-end performance.
1. Only the JavaScript required on a particular page will be added to that page. In
particular, by default Drupal doesn't need JavaScript on most pages that anonymous
users can see. This means that jQuery is not automatically loaded on all pages
anymore.
So, if your theme requires jQuery or some other JavaScript to be present (which also is
defined in an asset library), you need to tell Drupal that this is the case, by declaring a
dependency on the needed asset library.
2. The Drupal.settings javascript object is replaced by drupalSettings.
The process
The general steps for loading assets (CSS/JS) are:
But in the case of themes, there is an alternative to step 3: themes can choose to load any
number of asset libraries on all pages.
Defining a library
To define one or more (asset) libraries, add a *.libraries.yml file to the root of your
module folder (alongside your .info.yml file). (If your module is named fluffiness, then the
file name should be fluffiness.libraries.yml). Each "library" in the file is an entry
detailing CSS and JS files (assets), like this:
cuddly-slider:
version: 1.x
css:
layout:
css/cuddly-slider-layout.css: {}
theme:
css/cuddly-slider-theme.css: {}
js:
js/cuddly-slider.js: {}
You may notice the 'layout' and 'theme' keys for css which is not present for js. This indicates
the style type the css file belongs to.
base: CSS reset/normalize plus HTML element styling. Key assigns a weight of
CSS_BASE = -200
layout: macro arrangement of a web page, including any grid systems. Key assigns a
weight of CSS_LAYOUT = -100
component: discrete, reusable UI elements. Key assigns a weight of CSS_COMPONENT
= 0
state: styles that deal with client-side changes to components. Key assigns a weight
of CSS_STATE = 100
theme: purely visual styling (“look-and-feel”) for a component. Key assigns a weight
of CSS_THEME = 200
This is defined by the SMACSS standard. So here if you specify theme it means that the CSS
file contains theme related styling which is pure look and feel. More info here. You cannot
use other keys as these will cause strict warnings.
This example assumes that the actual JavaScript cuddly-slider.js is located in the
subfolder js of your module. You can also have the JS come from an external URL, include
CSS files, and there are other possibilities. See CDN / externally hosted libraries for details.
However, remember that Drupal 8 no longer loads jQuery on all pages by default; Drupal 8
only loads what's necessary. Therefore, we must declare that our module's cuddly-slider
library declares a dependency on the library that contains jQuery. It is neither a module nor a
theme that provides jQuery, it's Drupal core: core/jquery is the dependency we want to
declare. (This is an extension name followed by a slash, followed by the library name, so if
some other library wanted to depend on our cuddly-slider library, it'd have to declare a
dependency on fluffiness/cuddly-slider, because fluffiness is the name of our
module.)
So, to ensure jQuery is available for js/cuddly-slider.js, we update the above to:
cuddly-slider:
version: 1.x
css:
theme:
css/cuddly-slider.css: {}
js:
js/cuddly-slider.js: {}
dependencies:
- core/jquery
As you'd expect, the order the CSS and JS assets are listed is also the order in which they will
be loaded.
But what matters most is that we don't decide whether to attach a library based on which page
we're on (i.e. which URL or route), but based on which things are visible on the page: if a
page contains a '#type' => 'table', a '#type' => 'dropbutton' and a '#type' =>
'foobar', then we'll only load the libraries associated with each of those '#type's.
But we're not limited to '#type' only: perhaps we want to load a certain asset library only for
a certain instance of a '#type'. In that case, we just attach it to the render array of that
instance.
Of course, very rarely, there is a valid reason to actually load a certain asset on all pages (e.g.
some analytics JavaScript that tracks page loads), regardless of the "things" on a page.
To attach a library to a certain existing '#type', for all instances of it, we use
hook_element_info_alter():
Then clear the cache so that Drupal is aware of the new hook implementation you added.
To attach a library to a render array (and perhaps a specific instance of a certain '#type'),
you must have access to that render array. Perhaps you're defining the render array. Perhaps
you're modifying it in a hook. In either case, it will look somewhat like this:
$build['the_element_that_needs_the_asset_library']['#attached']['library'][
] = 'your_module/library_name';
return [
'#theme' => 'your_module_theme_id',
'#someVariable' => $some_variable,
'#attached' => array(
'library' => array(
'your_module/library_name',
),
),
];
As forms are just render arrays, attaching a library works just the same:
/**
* 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';
}
}
}
In some cases, the asset library is not associated with a certain part of the page, because it is
associated with the entire page. For this case, hook_page_attachments() exists. A clear
example can be found in the Contextual Links module:
// 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';
}
You can attach a library in a preprocess function using the special key '#attached':
function yourmodule_preprocess_maintenance_page(&$variables) {
$variables['#attached']['library'][] = 'your_module/library_name';
}
You can also attach a library in a twig template by using the attach_library() twig function. So
in any *.html.twig:
{{ attach_library('your_module/library_name') }}
<div>Some markup {{ message }}</div>
In some cases, you may want to add JavaScript to a page that depends on some computed
PHP information. You can do so with drupalSettings (the successor to Drupal 7's
Drupal.settings), an array of settings defined in your PHP script that can be accessed as
settings object in your JavaScript.
cuddly-slider:
version: 1.x
js:
js/cuddly-slider.js: {}
dependencies:
- core/jquery
- core/drupalSettings
In our PHP files, we can now pass the desired drupalSettings alongside our library. By
convention, we use our lowerCamelCase module name as the key for the settings, and add the
lowerCamelCase name of the library as sub key.
If we'd like to pass computed values 'foo' and 'baz' from PHP to our example's JavaScript,
we could do:
$computed_settings = [
'foo' => 'bar',
'baz' => 'qux',
];
$build['#attached']['library'][] = 'your_module/library_name';
$build['#attached']['drupalSettings']['fluffiness']['cuddlySlider'] =
$computed_settings;
If you want to add attributes on a script tag, you need to add an attributes key to the JSON
following the script URL. Within the object following the attributes key, add the attribute
name that you want to appear in the script as a new key. The value for this key will be the
attribute value. If that value is set to true, the attribute will appear on its own without a value
on the element.
For example:
https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libr
aries=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>
Disabling Aggregation
By default, multiple local files will be aggregated where possible. To disable this for a file, set
its 'preprocess' flag to false.
cuddly-slider:
version: 1.x
js:
js/cuddly-slider.js: {preprocess: false}
dependencies:
- core/jquery
- core/drupalSettings
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 is highly discouraged. It's recommended to put the JS you want to use
inline in a file instead because that allows that JavaScript to be cached on the client side. It
also allows JavaScript code to be reviewed and linted. Inline JS with also conflict with the
Content Security Policy of many sites and make your module unusable by them.
This is discouraged. Place the javascript in a file instead. Examples of this are ads, social
media sharing buttons, social media listing widgets. These do use inline JavaScript. But they
are just a special kind of content/markup, since they're not about decorating the site's content
or making it interactive, instead they are about pulling in external content through JavaScript.
You want to put these in either a custom block or even directly in a Twig template.
E.g.:
<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':'htt
ps';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://p
latform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(docu
ment,"script","twitter-wjs");</script>
Inline JavaScript is highly discouraged. Examples of inline JavaScript that affects the entire
page are analytics (e.g. Google Analytics) and hosted font services. Inline JavaScript that
affects the entire page can be in either of two categories: front-end/styling, or logical. Most of
these cases can be satisfied with fixed javascript in a file plus added settings.
In the case of front-end/styling (e.g. hosted font services), it belongs in the theme, and for
that, please see “Adding stylesheets (CSS) and JavaScript (JS) to a Drupal 8 theme”.
In the other case, the JS belongs in the module. In the appropriate hook — likely
hook_page_attachments() — define attached HTML <HEAD> data by using the
'html_head' key in the #attached property:
],
// A key, to make it possible to recognize this HTML element when
altering.
'hello-world',
];
}
If the dynamic CSS/JS is used across multiple requests, then you can use
hook_library_info_alter() to modify a library to include your dynamically/automatically
generated CSS/JS. An example in Drupal 8 core of this is color_library_info_alter().
Realize that just using hook_library_info_build() or hook_library_info_alter() to
append a library will not automatically make the library appear in the page. You still have to
define it as an attachment (either for the page or for a certain element) by using any of the
techniques above.
If the dynamic CSS/JS is built for each request, then you enter the truly advanced territory.
This is hard, and for a good reason: per-request dynamic assets have to be built on every
single request and therefore slow Drupal down. We want to make it hard to make Drupal slow
down, so this is why we don't offer a nice API for this — since we don't want you to do it.
It is possible though. In the case of dynamic JS: please consider using configurable
JavaScript instead, that is almost always the much better choice. Then the logic is stored in a
file (and can be reviewed, linted and cached on the client side), and only the settings to
configure the logic in that file need to be generated on each request. And in fact, this can also
be used for dynamic CSS: attach dynamic CSS as drupalSettings and let some JS file add it
to the page.
If using drupalSettings plus a JavaScript file is not an option, then you still have one option
left: use hook_page_attachments(), where you add a new value to
$page['#attached']['html_head'], which contains either a <script> tag or a <style>
tag, as the “Inline JavaScript that affects the entire page” section above already showed.
For some advanced use cases — like detecting 3rd party libraries that need to be downloaded
manually, and then exposing those as Drupal asset libraries (think Libraries API module) —
you want to be able to still use PHP code to register libraries using some additional logic.
That's why hook_library_info_build() was added
Note that "dynamic" doesn't mean "runtime" (i.e. for every request) — that'd be terrible for
performance. The dynamically added libraries are still cached, just like libraries defined in
YML files. This means that you still need to attach the library to a page or element using any
of the above techniques. It's "dynamic" because you can use logic to control this attaching of
the libraries.