fromMarch 2012

Semantic Views

Controlling Your Markup in the Views UI

The Drupal Views module has probably seen the most explosive growth of any module in the entire Drupal ecosystem. Originally started all the way back in Drupal 4.6, the first Views UI did not even have the ability to rearrange fields. If your fields were out of order, you had to remove them and re-add them in the order you wanted. Over several iterations and several versions of Drupal core, Views has added more and more features and a robust API so that other contributed modules could make use of its query building and rendering engine. Originally, the rendering engine gave absolutely no control of the markup whatsoever, requiring users to customize their CSS to suit Views. As Views has evolved, it has moved away from using overrides and toward specifying controls directly in the interface.

Drupal 5 included a system called the Views Theme Wizard which could generate a set of templates that could be added to a theme and would allow control of a view's markup. The downside was that once this template was generated, changes to the view in the UI might not be recognized, since all the fields being rendered would now be hardcoded in the theme. And since Drupal 5 did not have true module-owned templates (only themes could have templates), it was very difficult to get the right data into a template without code generation.

For Drupal 6, I actually re-tooled the Drupal core templating system specifically to provide more flexibility to Views users. This allowed site builders to override templates for individual theme templates with a pattern matching system, leading to the interesting naming scheme that created files such as views-view-unformatted--tracker--block-1.tpl.php. This made the theme wizard obsolete. The downside: the template that rendered all of the fields for a view would loop through the fields, and it was very difficult for a front-end developer to discern how to affect an individual field.

Well into the maturity of Drupal 6, Benjamin Doherty (also known as bangpound on and IRC) wrote a module called Semantic Views. This module came into being shortly before DrupalCon Paris. It was presented to me at DrupalCon as a method to more easily control the markup of a given view without having to override templates. At the time, I nodded and smiled and said it sounded very good for front-end developers who find deeply nested DIV tags embarrassing. But for people who aren't so into the nuts, bolts, and magic incantations of HTML and CSS, it seemed irrelevant. So at first, this module was simply left on its own and it developed a quiet following; after all, the real power of Views is its modularity.

Then, over time, more and more people used this module in their everyday site building. But as its popularity grew, people ran into the limitations posed by it being a module instead of built into Views. As a style plugin, it could only affect things which that style plugin understood, which more or less amounted to the unformatted style and the HTML list style. So tables couldn't be redone semantically. Some of the features it offered started to seem very attractive, particularly complete control of the classes on every field and row in the output. People started to become reliant on the ability to do things like put titles in H2 tags and, coupled with the already strong power of rewriting Views' native field rewrite, vast amounts of control were becoming available. But as an external module, this power had limits.

As more people used this, and I saw the results of this usage, I relented and, as part of Views 3, I put a significant amount of work into Views to enhance fields and completely change how the markup system worked. After several iterations, the team and I came up with something we were all very happy with!

The biggest weakness of Semantic Views was that it was a style plugin. This convention caused a significant amount of configuration for fields to be separated into different parts of the UI. Sometimes this was actually convenient but usually it was not. It also limited what types of configuration you can have. In Drupal 7, we took the features out of the module and applied them to the places they affect. The bits that affect fields are now in the field UI, and the bits that affect the row and the view itself are in the view.

One of the first problems we had to address was that we wanted to make sure we did not break people's existing markup during the upgrade. All of the old <div> and <span> tags and views-generated class names needed to remain. But it turned out to be quite a chore to disable all of these on every field when you didn't want them. Imagine a view with fifty or sixty fields and having to check the same set of boxes on every one. This turned out to be easy enough to fix with a single checkbox in the row style settings. When creating a new view, if you want full control over the HTML, this checkbox is your friend.

Wrapper Elements

By default, this is checked in all existing and all new views. All old views retain their original markup, and new views can very easily be told not to use any markup on their fields at all. And by no markup, we mean no markup. The initial configuration of a field will not put a single <div> or <span> element around the field; the most you will get is necessary markup for the style—for example, tables will still have their <td> tags to define the cells.

In Views, every field has three pieces of markup you can affect. The first is called the Field HTML and it is the HTML element that is used to wrap the field itself. The configuration for it is a select box that contains a few basic HTML element types, including div, span, strong, em, h1-h6, p and none. It also contains the default option. The default option will use the behavior as defined by the style checkbox, above, which is either the default <div> or <span> or nothing.

It also includes a checkbox labeled "Create a CSS class". If checked, a custom CSS class can be utilized. This class can also be somewhat dynamic. We'll discuss that more a little later.

Style Settings

The next piece of markup that can be controlled is the label HTML, which is the wrapper that goes around the label. Normally this is a <span> that is designed to make the label bold, but many fields either do not have a label or want specialized behavior for the label. Finally, there is the HTML that wraps both the field and the label, and is the primary field wrapper.


Note that tables are a little bit special when it comes to labels. They do not print the label with every field, they print it only once in the header. As a consequence, the Field and Label HTML wrapper is not present. Since fields can exist on multiple displays, and these displays can have different styles, the style is unable to determine if this will not be relevant and the checkbox appears even if it will not be displayed.

There is also a checkbox to control whether or not Views adds its default field classes. These can be very useful for targeting fields, but there are times when it is very convenient to simply add known classes to the fields, particularly if it would be beneficial to have the markup provided by Views match the markup provided by a template, so as to re-use the CSS already provided. When all this is combined, it is quite simple to tailor the markup of fields as needed.

Semantic Output

In addition to every field element having a custom class, every row can have a custom class. Since a single custom class for every row is not nearly as useful as we would like, another very useful feature is: you can use replacement tokens as classes.

In Views, replacement tokens are unrelated to token.module, which handles Drupal's internal token replacement language. Instead, tokens are actually generated from fields. In the Views UI, you can add a field to the view, set it to 'exclude' (which means it won't actually be rendered as part of the normal display). Once the field exists, it will provide a token which can be used in other fields, but also in classes for fields and the rows.


You can use two "Global: Custom" fields to create wrappers for groups of fields. Add the custom field, remove the label, and set the text to '<div class="someclass">', and set the administrative name to 'DIV start'. Add another "Global: Custom" field and set its text to '</div>' and its administrative name to 'DIV end'. Use the rearrange button to move them around the fields you want grouped; you can easily put multiple fields into a box that you can position or style appropriately with CSS.

For example, when displaying a node for some purpose, you may want the node styled differently if the sticky flag is set. First, add the sticky field and set it to exclude. In the rewrite settings for the field, rewrite the field to "node-sticky". Then, in the no results behavior section, make sure that "Hide if empty" is checked, along with "Hide rewriting if empty". Finally, put the token [sticky] in the row class in the Format settings. After all this is done, all nodes the view displays that are sticky will have the "node-sticky" class, while other rows will not have that class.

This technique also works very well for taxonomy terms or Field API select lists, as you can use this field to form the basis of a class. If things do not map perfectly, sometimes a PHP Computed Field can be used to remap a field or group of fields into something usable for a class.