0

Is it possible to alter html_head using hook_page_attachments_alter() per node? I need to attach different <script> tags to a list of nodes. I am able to add it to each node, but all gets overridden with the last alteration.

$nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties(['type' => 'faq_page', 'status' => 1]);

foreach($nodes as $node) {
  $description = [
    [
      '#tag' => 'script',
    ],
    // …
  $page['#attached']['html_head'][] = [$description, 'description];
}
6
  • 2
    What is the list of nodes? And how is it connected to the page content, an entity route of a single node?
    – 4uk4
    Mar 23, 2021 at 20:42
  • How the hook works in general and what else might be helpful especially in this case for multiple nodes see drupal.stackexchange.com/questions/288826/…
    – 4uk4
    Mar 23, 2021 at 20:44
  • @4k4 that is great point that I forgot to include. I'll update the post with that information, but it is $nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties(['type' => 'faq_page', 'status' => 1]); Mar 23, 2021 at 20:50
  • Are those nodes also part of the page? Then it might be a better idea to add the attachments to the rendered nodes itself instead of querying the nodes again.
    – 4uk4
    Mar 23, 2021 at 21:00
  • 2
    My argumentation is that the attachments then belong in the corresponding node as discussed in the linked topic. The connection I asked about is still unclear, but it doesn't really matter. Everyone who is reading this should try to avoid using this hook and attach metadata to render elements, #cache + #attached, they both work together.
    – 4uk4
    Mar 23, 2021 at 23:29

1 Answer 1

2

Yes, when you get the current node.

/**
 * Implements hook_page_attachments_alter().
 */
function MYMODULE_page_attachments_alter(array &$attachments) {

  $node = \Drupal::routeMatch()->getParameter('node');

  if ($node instanceof \Drupal\node\NodeInterface) {

    $attachments['#attached']['html_head'][] = [
      [
        '#tag' => 'script',
        '#value' => 'alert("' . $node->getTitle() . '")',
      ],
      'MYMODULE_foobar',
    ];
  }
}

If this really should be in a foreach, you need to ensure to give each tag an unique key.

foreach ($nodes as $node) {
  $description = [
    [
      '#tag' => 'script',
    ],
    ...
  ];
  $page['#attached']['html_head'][] = [$description, 'description_' . $node->id()];
}
4
  • Thank you so much! I got it to work with your help :) Mar 23, 2021 at 20:52
  • How would I make that script attach to that node only? With the current structure, scripts get attached to all nodes in that array. Mar 24, 2021 at 15:50
  • @PhilipPark – Sorry, I don't understand what that means. Maybe have some if-requests checking for the node ID, node type or something like that before you enter the logic?
    – leymannx
    Mar 24, 2021 at 16:09
  • @PhilipPark – At best you have a Views block querying the nodes you want, place that block somewhere on the page and build the schema.org markup directly there in that block template. It doesn't necessarily have to be in the head right? But even if, hook into the View and attach the head script from there then.
    – leymannx
    Mar 24, 2021 at 16:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.