You are here

How to add title as data attribute to menu links in Drupal 8 and Twig

Data attributes on HTML elements can be very handy for targeting certain elements with CSS and/or Javascript.

Let's say, for example, we have a site with a menu whose links have a different background colour depending on what category they belong to, cats or dogs.The HTML might look like this:

<ul class="menu">
  <li class="menu-item">
    <a href="#">Cats rule</a>
  </li>
  <li class="menu-item">
    <a href="#">Dogs are the best</a>
  </li>
</ul>

All "cat" menu items should have a background colour of blue, and all "dog" menu items a background of green.

We want this to happen automatically, without site editors having to do anything extra, and without adding a module. At first I thought I could target the menu items by their text content using CSS attribute selectors, but you can't target text nodes with CSS.

You can target the href attribute, so I do something like this:

ul.menu a[href*="cat"] {
  background: blue;
}
ul.menu a[href*="dog"] {
  background: green;
}

However, we don't necessarily know if the URLs will match up with the menu item text. So I set about finding a way to add a data-attribute to the menu items that will be set to the link title, so I can then target the data attribute with CSS.

We're going to be editing menu.html.twig, found inside your Drupal core theme (probably Classy or Stable) templates/navigation folder. Don't edit that file—copy it into your theme's "templates" folder first, then edit the copied file.

We're going to be working inside the "for" loop that prints out all the menu items:

{% for item in items %}
      {%
        set classes = [
          'menu-item',
          item.is_expanded ? 'menu-item--expanded',
          item.is_collapsed ? 'menu-item--collapsed',
          item.in_active_trail ? 'menu-item--active-trail',
        ]
      %}
      <li{{ item.attributes.addClass(classes) }}>
        {{ link(item.title, item.url) }}
        {% if item.below %}
          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      </li>
{% endfor %}

What we want to do is add a data-attribute to the <li> element with the item.title variable, which is the text of the menu link.

First, we create a new variable which we set to the item title, then we apply the clean_class filter which lowercases it and replaces any spaces with hyphens:

{% set link_title = item.title|clean_class %}

(I first tried some of the other Twig filters like |lowercase and |replace, but it appears they don't work inside variables. I also couldn't use them inside the data attribute I added to the list item. I may have been doing something wrong, but |clean_class works).

We then add a new attribute, data-title, to the list items, the value of which is set to our link_title variable:

<li{{ item.attributes.addClass(classes).setAttribute('data-title', link_title) }}>

(For more info, see "Using attributes in templates" in the Drupal 8 guide).

The entire piece of code:

{% for item in items %}
      {%
        set classes = [
          'menu-item',
          item.is_expanded ? 'menu-item--expanded',
          item.is_collapsed ? 'menu-item--collapsed',
          item.in_active_trail ? 'menu-item--active-trail',
        ]
      %}
      {% set link_title = item.title|clean_class %}
      <li{{ item.attributes.addClass(classes).setAttribute('data-title', link_title) }}>
        {{ link(item.title, item.url) }}
        {% if item.below %}
          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      </li>
{% endfor %}

After clearing caches, we now see in our menu:

<ul class="menu">
  <li class="menu-item" data-title="cats-rule">
    <a href="#">Cats rule</a>
  </li>
  <li class="menu-item" data-title="dogs-are-the-best">
    <a href="#">Dogs are the best</a>
  </li>
</ul>

and we can write our CSS like this:

ul.menu li[data-title*="cat"] {
  background: blue;
}
ul.menu li[data-title*="dog"] {
  background: green;
}

Add new comment

Full HTML

  • To post pieces of code, surround them with <code>...</code> tags. For PHP code, you can use <?php ... ?>, which will also colour it based on syntax.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.