Front-end engineer with a passion for learning something new every day

Drupal 8 Architecture: How to Add Custom HTML Data Attributes to Menus

In a new Drupal 8 build that I am working on, there is a use case for some menu items to have an abbreviation underneath. I wanted to create a content editor friendly way of achieving this in the Drupal menu admin UI. Since I have already implemented the Drupal 8 Link Attributes widget module, I got to thinking that perhaps I could extend the module to handle these abbreviations.

Extending the Link Attributes widget

I was happy to discover that Link Attributes can be extended to any kind of additional HTML attribute you might need for your menu. I came up with the idea that if I could add a new field for the abbreviation, I could turn it into an HTML data attribute for a menu item and then output it with a little jQuery.

There are two pieces to extending the widget within a custom module, my_module.

  1. Leverage function my_module_entity_base_field_info_alter to add the new attribute desired.
  2. Create a custommy_module.link_attributes.ymlfile and add your attribute in there.

Function

Our function looks like this:

function my_module_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  if ($entity_type->id() === 'menu_link_content') {
    $fields['link']->setDisplayOptions('form', [
      'type' => 'link_attributes',
      'settings' => [
        'enabled_attributes' => [
          'id' => FALSE,
          'name' => FALSE,
          'target' => TRUE,
          'rel' => FALSE,
          'class' => TRUE,
          'accesskey' => FALSE,
          'data-menu-abbreviation' => TRUE,
        ],
      ],
    ]);
  }
}

YAML

The key from the above code is 'data-menu-abbreviation' => TRUE, to let Drupal know, we want a field with that name. Once that is done, we can write a little code in our my_module.link_attributes.yml file as to how this will look in the menu UI. Note that we match the attribute name from entity_base_field_info_alter.

data-menu-abbreviation:
  title: Menu Abbreviation

Once you clear cache, the menu UI will now show the new field we created.

The Drupal 8 menu admin UI showing the new field for an abbreviation attribute
The Drupal 8 menu admin UI showing the new field for an abbreviation attribute

Because we use data- here, this attribute will render as an HTML5 data attribute which is ideal to leverage with jQuery. Inspecting on the front end, we see the menu item render as:

Rendered HTML

<a href="" class="custom-menu__link" data-menu-abbreviation="DRPL">
  A menu link with an abbreviation
</a>

This is a great, we now have our abbreviation within the menu link. The next step is to read the data in jQuery and render that as a span tag within the menu item.

Pull the data in via jQuery

Now in our custom theme JS file, we can loop through the menu items and output each data attribute like so:

// Get the menu data attribute and render as a span tag.
$(context)
  .find(".custom-menu__item--level-1 > a")
  .once("menu-abbreviation")
  .each(function () {
    // If there is a data attribute, "menu-abbreviation".
    if ($(this).data("menu-abbreviation")) {
      // Set a variable.
      var menu_abbreviation = $(this).data("menu-abbreviation");
      // Output the tag from the data.
      $(this).append("<span>" + menu_abbreviation + "</span>");
    }
  });

After this code, we now see the output look like this:

<a href="" class="custom-menu__link" data-menu-abbreviation="DRPL">
  A menu link with an abbreviation
  <span>DRPL</span>
</a>

This works great and solves a very specific use case but as you can imagine, this type of customization could come in handy for a lot of menu needs.

Gotchas

There are a few gotchas here, it sounds like hook_entity_base_field_info_alter may be changed or deprecated in Drupal 9 so be aware of that when upgrading. (see the issue listed below in resources). You also might need to use hook_module_implements_alter to adjust the weight of your custom code to be sure it fires after link_attributes if your module is named with a letter starting before the letter "L".

Resources

Tags

Read other blog posts