Drupal - How do I include JavaScript on a single page in a way that is amenable to scale

Solution:

Following Steps will make working with External JavaScript libraries in Drupal 8 absolutely clear (including how to conditionally load it in specific pages):

Step 1: Place the JavaScript file in your theme or module inside a folder called js. After this step your folder structure must look like :

my-theme/js/my-script.js

Step 2: If your theme already has a my-theme.libraries.yml file open it, if not create it.

In Drupal 8 Libraries are simply collections of CSS or JS files bundled together under a uniquely identified library name.

In this step we need to include the JS file that we created in step 1 in our library. we do it by adding following lines of code in our my-theme.libraries.yml file.

my-library:
  js: 
    js/my-script.js: {}

Now we have an asset library called my-theme/my-library which can be loaded to our site in several ways.

Step 3: Final step is to load the library in our site. This can be done in 4 specific ways:

Way 1: Attach library globally via theme-name.info.yml file

Libraries can be specified in the theme-name.info.yml file by adding it to the libraries property, like this:

name: my-theme
type: theme
description: my theme
package: other
core: 8.x
libraries: 
  - my-theme/global-effects
  - my-theme/my-library

base theme: stable

Any libraries specified here in the themename.info.yml file will be made available on evey page.

Way 2: Using hook_page_attachments_alter in theme-name.theme file.

Following lines of code will attach our library only to the node listing page.

function mytheme_page_attachments_alter(&$page){
  $path = $current_path = \Drupal::service('path.current')->getPath();
  if($path == '/node') {
    $page['#attached']['library'][] = 'my-theme/my-library';
  }
}

Way 3: Another useful way to conditionally add a library is by using a preprocess function like hook_preprocess_page().

Here's another example of restricting our asset libray to the front page

function mytheme_preprocess_page(&$variables){
  if ($variables['is_front'] == TRUE) {
    $variables['#attached']['library'][] = 'my-theme/my-library';
  }
}

Way 4: Asset libraries can also be attached from within a Twig template using the attach_library function. Anytime that template is used the corresponding library will be attached in accordance with any template-specific conditions.

In order to see this in action, we need to create or modify a template in our theme. In this example let us modify node.html.twig template to attach our library only if node.id == 1 with following lines of code

{% if node.id == 1 %}
  {{ attach_library('my-theme/my-library') }}
{% endif %}

With this in place, visiting node 1 in our site, we will be able to see our library in action.


See: https://www.drupal.org/developing/api/8/assets

Quote from page:

Attaching a library to page(s)

Depending on which assets you need to have loaded, you'll want to attach the corresponding asset library in a different way. After all, some asset libraries are needed on all pages, others only very rarely, and yet others on most, but not quite all.

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.