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:
- 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. - 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. - 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:
- Setting the
active
class is now an opt-in feature:l()
andLinkGenerator::generate()
now have aset_active_class
option that defaults toFALSE
theme_links()
now accepts aset_active_class
key that defaults toFALSE
Those links that you'd expect to get the
active
class set, do get it: menu links, language switching links … - The output of
l()
,LinkGenerator::generate()
andtheme_links()
no longer contains theactive
class, even if you do opt in. Instead, they get adata-drupal-link-system-path
attribute, and if there's a GET query, they also get adata-drupal-link-query
attribute. The link's language can be found in thehreflang
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 whatsystem_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.)
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.