Over a million developers have joined DZone.

HTML Heading Tags Don't Know What They Are Anymore

DZone's Guide to

HTML Heading Tags Don't Know What They Are Anymore

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

Last weekend, I set out to write a posting for this site. In the course of working on that, I ran into this old problem again, and got absorbed in trying to solve it. I'd had some ideas on it a couple of years ago, but had never gotten around to trying them out. The result is this posting instead of the one I had originally intended to write. I'll still get to that post later....

What's Wrong with Heading Tags?

Semantic Markup

We've been told for some time now that we shouldn't use HTML tags to directly format content. Instead, we're supposed to be using tags as delimiters for different kinds of content, and we're then supposed to use those delimiting tags to format content via CSS selectors. The notion is that tags are a form of semantic markup, and that we're using this technique to keep content separate from presentation. (If you're not familiar with this concept, you can get a feeling for it by visiting the CSS Zen Garden.)

The theory is good: you should be able to use CSS to reskin content on your site in any way. I once worked on a large media publishing system which would capitalize on this by making news available in the form of pure NewsML, thereby allowing various properties to format it to suit the look and feel of their pages.

Textbook-Style Content

Textbook-style content is broken down into sections. Sections can have sub-sections, which can in turn have more sub-sections. Here are some examples:

These examples aren't using nested sections, but I've run into a number of cases where I wanted to do that. I've done it on this page to demonstrate the effect.

HTML Heading Tags

I tend to write a lot of wiki documentation for projects I'm working on, and I like to use the textbook style described above. This documentation is hand-crafted in the target wiki. The sections are helpful because they delineate concepts in a useful way, and I make the section headings named anchors so that I can send people links to them.

HTML provides what I'll call the heading tags: <h1>-<h6>.

These appear to be what I need to use to achieve this effect. Each section is headed by one of these. However, in retrospect, they violate the semantic markup principle described above. I wasn't aware of that at first. For some time I used these, but did notice that editing the documents I was working on involved varying degrees of pain because of these tags. I found that I would move sections around, sometimes taking a top-level section, and making it a sub-section of another. Or, sometimes the reverse: take a subsection, and make it a top-level section.

These section moves were painful because I would have to hunt down the heading tags, and adjust them: when moving an <h3> subsection up a level, I have to remember to change it to an <h2>. This is error prone: sometimes I would miss them in the flat list they appeared in. Or, sometimes I would forget to convert the closing tag.

HTML Heading Tags aren't just Semantic

I recently realized that using the heading tags borders on specifying formatting. Oh, it's not as specific as adjusting a font size with something like the <font> tag (now considered a big no-no). But heading tags are specifying the level of the content. And the level can be determined from the structure of the document -- we shouldn't rely on humans for that. If the level were inferred from the structure of the document, then authors could move content at will without having to worry about indicating (or changing) what level it is at.

The upshot is that heading tags aren't just saying "this is a heading," which would be pure semantic markup. They are saying "this is a heading at level 3," or whatever level, and that implies a difference in formatting.

HTML5 to the Rescue?

When I ran into this old problem again, it occurred to me that HTML5 might have finally done something about this. So I did a few quick searches. Here is what I turned up:

  • How does Google interpret multiple H1 tags in nested HTML5 sections?
    Sounds promising. Seems to imply that the new <section> tag lets all headings be <h1>, solving my problem.
  • html5 Doctor: The section element
    You're not supposed to use <section> on content that needs to be styled. Isn't that pretty much everything?
  • When to Use the HTML5 "section" Element
    I'm confused. "... numbered sections of a thesis" sounds like what I want. But we still have "...styling purposes...use the div element instead." This page seems to go back and forth. Finally, "View Source" reveals that this page doesn't use <section>, and has three different levels of heading tags for the "section" headers in the content. Apparently these experts don't believe <section> should be used for that.

The last case cinched it, and I concluded this problem still hasn't been solved.

A Substitute for Heading Tags

My requirements are simple: I should be able to markup sections in my document as sections, and indicate what the section heading should be. That markup should be independent of where in the document the section appears.

Disclaimer: I am a back-end server kind of guy, and definitely not a DHTML/JavaScript Ninja. There may well be a solution or better answers to this, but it's not really my focus, and I just dabble in this when I need to. If you know better ways to accomplish this, please let me know.

The simplest thing I could think of looks like this:

<div class="sectiontoc-section">
<span class="sectiontoc-title">Section Title Goes Here</span>
<p>Section content goes here...</p>
<div class="sectiontoc-section"> <!-- you can nest sections -->
<span class="sectiontoc-title">Subsection Title Goes Here</span>
<p>Subsection content goes here...</p>


In order to make it work, I would just have to create styles for the classes in the markup.

However, it wouldn't be that simple. I wanted to use this scheme on this site, which is generated by Drupal. I'm using a canned theme I downloaded, and it already has a stylesheet for these. I don't want to have to duplicate all the heading tag styles on sets of nested class selectors. What if they change? What if I want to use this with another theme?

I can take advantage of the existing styles if I can turn the markup above into heading tags. But I don't want to do that by hand. So, JavaScript to the rescue: I wrote some functions to walk through the <div>s above, and to replace the <span>s with heading tags based on their depth within sections.

I built a test page I could load locally via file:// in order to test the JavaScript:

At the same time, I took advantage of my walk through the sections to (optionally) produce a mini table of contents which links to the section headings via named anchors. Even if I'd used a stylesheet for the headings, I would still have had to do something like this in JavaScript.

Drupal Integration

Once I had the JavaScript sample above working, I thought my problems were over. Unfortunately, I then had to figure out how to get that loaded for a Drupal site.

Unfortunately, most of the Drupal documentation is about how to write PHP to generate content for a module. All I need is for the .js file to be loaded, and for an onload event to trigger it to transform the page. Drupal uses jQuery pretty heavily, and you have to integrate with that. The only reference I found was Managing JavaScript in Drupal 7, which pointed at some examples. After looking through those, and parts of Creating Drupal 7.x modules.

In my first attempt to hook this up I just transplanted the code above into a Drupal behavior for $(document).ready(...), but that didn't work. After poking around at it a bit, I sought help in Drupal pages and on drupal.stackexchange.com, but no one there was able to help me out. Poking around some more, I managed to use the Chrome JavaScript debugger to see what was going on, and found that many of my variables were undefined. Apparently jQuery doesn't like it if you use plain old DOM methods from within.

I skimmed a few jQuery tutorials on the web. I converted my original JavaScript so that the loops were replaced with jQuery selectors, and the loop bodies became anonymous functions. I did this outside Drupal, and ended up with these:

In order to make it work, I had to wrap all of the content with one more <div> so that I could figure out the content depth; my original supposition that these would be directly inside the <body> was wrong within the context of Drupal. That made the final result look like this:

<div id="sectiontoc-body">
<div id="sectiontoc-toc"><!-- placeholder for table of contents --></div>
<div class="sectiontoc-section">
<span class="sectiontoc-title">Section Title Goes Here</span>
<p>Section content goes here...</p>
<div class="sectiontoc-section"> <!-- you can nest sections -->
<span class="sectiontoc-title">Subsection Title Goes Here</span>
<p>Subsection content goes here...</p>
</div> <!-- secctiontoc-body -->

Finally, I cobbled together a Drupal module, installed it in the usual way, enabled it, and voila! If you view source on this page, you can see that I've used the sectiontoc- ids and classes in the markup; the displayed page uses appropriate headings, without the author having to know their depth when authoring. The page has the table of contents at the top, and these are linked to named anchors in the body.

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.


Published at DZone with permission of Chris Westin, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.


Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.


{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}