Fluffy does Bad Things with his Friends
When we last left Fluffy on Booplesnoot.dev he was pretty sessile. Sure, I told you that he liked his cat, his purple ball, and cookies, but you had to take my word for it. Like most bunnies though, Fluffy likes to do hood-bun stuff with his friends and for that, we need some JavaScript.
Creating Libraries
When developing themes, JavaScript is added in much the same way as CSS is. We discussed the process in my first post, Drupal 8 Theming: Part 1, but that was a while ago so let us recapitulate. We’ll break it down into steps so it’ll sound like my favorite bit by comedian Tommy Johnagin entitled Making a Baby and Chipotle, which basically says: “Step one, say you’re not going to do the thing. Step two, do the thing anyway”.
Step 1: Write some JavaScript
The first incredibly obvious thing we’ll need to do is write some JavaScript and save it as a file in the appropriate, and aptly named, JavaScript directory.
Ta da!
As an example, I’ve written some simple JavaScript that will allow us to give Fluffy a cookie.
Step 2: Define an Asset Library
An asset library is a collection of files consisting of one or more JavaScript and CSS files. You can also have libraries comprised solely of JavaScript OR CSS, respectively. As Captain Planet would have said circa 1990, the power is yours.
Libraries need to be declared in your libraries.yml file as follows:
Please note the two differences in declaring CSS libraries vs JavaScript libraries.
Theme key
The ‘theme’ key used when declaring CSS libraries is absent from the JavaScript library declaration. That key is there to assign a weight to the CSS. There are five keys, each one providing a different level of styling. However, only the ‘theme’ keyword should be used for theme styles, which provides a weight of 200; this is a SMACSS standard.
Here are all the styling levels:
- Base – CSS_BASE = -200
- Layout – CSS_LAYOUT = -200
- Component – CSS_COMPONENT = -200
- State – CSS_STATE = -200
- Theme – CSS_THEME = -200
Dependencies
In an effort to improve performance, Drupal 8 no longer loads jQuery on every page. Therefore if your JavaScript assets depend on jQuery to run, you’ll need to declare jQuery as a dependency like so:
jQuery is still included in Drupal’s core.
Step 3: Attach the Library
Depending on what the JavaScript you’ve written applies to there are different ways of attaching it, which we’ll delve into in a moment. However, since we’re working with themes in this post series, the most advisable way to include a library in a theme is to reference it in your theme’s info.yml file.
However, in the event that you have a library containing styles or functionality that you only want to apply to a specific template, the method of attachment you’ll want to use is the Twig ‘attach_library’ function. This function can be used to attach an asset library to any template page ending in ‘html.twig’. In the following example ‘fluffy’ is the name of our theme and ‘cookie-time’ is the name of the library containing our JavaScript.
Fluffy Loves Cookies
As mentioned previously, Fluffy is quite the pastry buff — especially where cookies are concerned. So, in the Complex Bunny page of my booplesnoot.dev site, I’ve added a button and some simple JavaScript that allows to user to give Fluffy a cookie and evoke an appropriate response from our cottontail.
Here’s the cookie-time code:
Don’t act like you’re not impressed…
Here’s Fluffy in action.
Render Arrays
Now, back to the other ways of incorporating JavaScript into your Drupal 8 site — there are several wonderful ones, and a just plain awful and ill-advised one that will henceforth be referred to as ‘The Big No-No’. We’ll touch on that deviant behavior but I’m not going into detail on how to do it in much the same way that I wouldn’t say, “a bomb causes extreme devastation–here’s how to build one”.
#type
To attach a library to all instances of a certain #type, use this hook.
Here’s an example.
Make sure to clear your cache after implementing this function so Drupal is aware of the new addition.
*Please note that Drupal will ‘de-dupe’ libraries so that if you attach the same library to multiple fields, it will still only be rendered once–let’s keep it DRY, folks.
Specific instance of #type
To define or modify JavaScript specific to a render array or a specific instance of a #type you need access to the render array, the necessary code will look like this.
Block Plugin
If you’re building a block plugin you can attach the libraries to the render array with the ‘build()’ function of your class, extending the BlockBase class.
Pages (all or subset of)
When an asset library is associated with an entire page rather than a specific part, use hook_page_attachments().
Configurable JavaScript
If you want to add JavaScript to a page that’s dependent on computed PHP, you create a JavaScript file and define it in the asset library just as before but you also need JavaScript settings. The JavaScript file will read those settings via drupalSettings (D8’s replacement for D7’s Drupal.settings). To make drupalSettings available to your JavaScript file declare it as a dependency.
‘bar’ is some calculated value
Adding Attributes to Script Elements
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 you want to appear on the script as a new key. The value for the key will be the attribute value, if it is set to ‘true’ the attribute will appear on its own without a value on the element. For example, this
will create the following.
The Big No-No
‘The Big No-No’ is more commonly known as…Inline JavaScript!! Inline JavaScript is the black-listed, no-good, very-bad way of implementing JavaScript. Inline JavaScript is a deplorable idea for multiple reasons, which we’ll cover shortly, but one key reason to keep in mind is this: adding JS to your site outside of libraries will exclude that file from Drupal’s built-in JavaScript aggregation. Again, we won’t be covering how to do it, just that it’s wrong and why. If you want to learn more about these Dark Arts, you can do it on your own time, friend-o.
Inline JavaScript is HIGHLY discouraged. While I can almost guarantee you know this, inline JavaScript is written within structuring markup like HTML. Best practices dictate that it should be stored in a separate file. Storing JavaScript separately allows it to be cached on the client-side for faster loading; it also allows the code to be reviewed and linted. In addition to those excellent reasons, storing JavaScript as a separate file can keep it from conflicting with the site’s Content Security Policy. This is incredibly important because inline JavaScript that does conflict with CSPs may render the module in question completely unusable.
Generating Markup
Examples of inline JavaScript that generate markup include ads and social media sharing buttons. This JavaScript isn’t used for decorative or interactivity, but pulling in content from JavaScript. If you find that saving this JavaScript in a separate file isn’t possible, then instead add it to a custom block or as part of a Twig template.
Affecting the Entire Page
Example of inline JavaScript that affect an entire page are Google Analytics and some font services. This JavaScript falls into two categories: styling and logical. In the case of inline JavaScript used for styling, as with some font services, the JavaScript should instead be saved in a separate file and added to the theme as part of an asset library. Where logical inline Javascript is concerned, the code should be saved in a separate file then added as necessary with the appropriate hook.
Externally Hosted Libraries
Drupal 8 is still game to use CSS and JavaScript from Content Delivery Networks (CDNs), especially since it improves page load speed. Include external libraries as follows, including the specified information about them.
Differences Between D7 and D8
Libraries
Drupal 7 libraries were defined with the ‘hook_library_info()’ function, but have been replaced by the ‘*.libraries.yml’ file in Drupal 8
Functions
The following functions have been replaced by ‘#attached’ in Drupal 8.
- drupal_add_css()
- drupal_add_js()
- drupal_add_library()
Settings
Drupal 8 settings are now stored in an external JSON file.
For the previous post in this series, click here.