Change record status: 
Project: 
Introduced in branch: 
8.x
Description: 

Previously, any and all links generated by l(), LinkGenerator and theme_links() would always set the active class on links (and also list items in the case of theme_links()).

Problem

While this is the simplest possible approach from an implementation POV, it comes with significant downsides:

  1. It breaks render caching: if a HTML fragment is reused on multiple pages, then the active class would only be correct for one page (the page it was generated on). In other words: it is incompatible with render caching. The fact that many — if not most — HTML fragments contain generated links implies that this feature in fact broke render caching in most of Drupal.
  2. It makes generating links stateful: the output of LinkGenerator::generate('magical pony', 'magical-pony') depends on the request context, i.e. a global is affecting the output of something that you'd expect to be deterministic, but because of the global affecting the output, it was in fact non-deterministic.
  3. It comes with a significant cost: checking for each link to see whether it's the active one is pretty expensive because often there are hundreds of links on a single page.

Solution

So, we did two things to increase performance:

  1. Setting the active class is now an opt-in feature:
    • l() and LinkGenerator::generate() now have a set_active_class option that defaults to FALSE
    • theme_links() now accepts a set_active_class key that defaults to FALSE

    Those links that you'd expect to get the active class set, do get it: menu links, language switching links …

  2. The output of l(), LinkGenerator::generate() and theme_links() no longer contains the active class, even if you do opt in. Instead, they get a data-drupal-link-system-path attribute, and if there's a GET query, they also get a data-drupal-link-query attribute. The link's language can be found in the hreflang attribute that was already being set on links. In other words: each link is annotated with metadata that allows us to determine whether it's active on the current page or not; we've made this deterministic and therefor more easily testable.

    Here comes the really interesting part: you can choose whether links are marked as active on either the server or the client! By default (see system_page_build()), this will happen on the client-side for authenticated users and on the server-side for anonymous users. But it's trivial to override this on your site (just override what system_page_build() does by default).

    (It's done on the server side for anonymous users by default because most sites either enable Drupal's page caching or use Varnish for anonymous users anyway, so Drupal might as well do the work so that we don't have to serve any JavaScript to anonymous users. For authenticated users, JavaScript is usually already being served already anyway, so we also choose to do the active link handling in JavaScript for authenticated users.)

  3. So this change enables better back-end performance (because we can now enable render caching on most things), makes Drupal's code more understandable & predictable (since it's deterministic), and gives you the option to completely disable active class handling if you don't need it.

Impacts: 
Module developers
Themers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done