Hi, I'm Yaakov Chaikin. I teach grad web development at Johns Hopkins University and on Coursera.org (1 MILLION students & counting!).
By day, I am a software developer.
thumbnail for article on HTML Content Models

HTML Content Models

6 min read |
| by Yaakov Chaikin

This article is part of the Beginner Web Developer Series. The series is targeted to people who’d like to start serious web development, as well as people who are already web developers and want to solidify their knowledge of fundamentals while possibly filling in some holes. If you find yourself tinkering with HTML, CSS, or Javascript until you sort of get it to work, this series is for you. The material in this series is closely tied to my top-rated Coursera course.

The term content model sounds fancy and complicated, but, in reality, it’s quite simple.

Content Model refers to the set of rules that define what type of content each element is allowed to have. Mostly, this translates into what other elements are allowed to be nested inside which other elements.

As you’ll see in a minute, it also refers to the default way the browser treats the content wrapped by a particular tag.

Prior to the modern HTML specification, HTML elements were either block-level or inline elements. Modern HTML specification split these two content models into seven models. So, things got a bit more complicated.

However, in practical terms, you can still think of those seven models as falling into the same traditional categories: block-level and inline elements. That’s why I am going to explain the differences between block-level and inline elements first, and then, I’ll show you how they roughly map to the new HTML content model categories.

Block-level Elements

Block-level elements render (i.e., are displayed) to begin on a new line by default. You can change that behavior with CSS but we’re not talking about CSS at this point, yet.

What that means is every time you specify a block-level element in HTML, the browser will automatically place the contents of that element on a new line in the flow of the document.

Block-level elements are allowed to wrap inline or other block-level elements.

Inline Elements

Inline elements render on the same line by default.

Your code can have a whole bunch of inline elements, one after another, but they will all still be displayed on the same line. Having new line characters in the content or between the tags in your code won’t make any difference. Remember, HTML doesn’t care about new line characters, spaces, etc. All those space characters get translated into a single space anyway.

Inline elements are restricted to only contain other inline elements. In other words, an inline element cannot have a block-level element as part of its content.

Modern HTML Content Models

As mentioned above, modern HTML specification replaces those two content model definitions with more fine-grained (complex?) definitions. However, the reason it’s still practical to group them into just those two is because they align very well with existing CSS rules.

So, as far as HTML coding is concerned, you can still pretty much think of all of them falling into either block-level or inline elements. Obviously, it’s a little bit of an over-simplification, but it works. 😀

Let’s take a look at a diagram that shows these new categories:

HTML Content Model Diagram

Looks a bit complicated, but there are a couple of things to notice about this diagram:

Just about everything is included as part of the Flow category.

Flow category roughly translates into the traditional block-level category. So, just like block-level elements, this diagram is showing that Flow elements can wrap (almost) all other elements.

The Phrasing category roughly translates into the traditional inline category.

Yes, there are good reasons why there are actually seven categories and not two. Most of the reasons, however, are semantic in nature. For example, the Heading category elements are not simply generic Flow or block-level elements. They also convey a meaning: the content they wrap is to be treated as heading content.

Code Example

Let’s take a look at some HTML code that will demonstrate these concepts.

Perhaps the most generic element in each category are the div and the span elements. The div element stands for division (as in divide the document into smaller parts) and is a common block-level (or Flow) element. The span element, which simply stands for span, is a common inline (or Phrasing) element.

Let’s go over the following example code (div-and-span.html) line by line. Pay particular attention to lines 8 through 15:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>div and span elements</title>
</head>
<body>
  <div>*** DIV 1: Some content here ***</div>
  <div>*** DIV 2: Following right after div 1 ***</div>
  <span>*** SPAN 1: Following right after div 2 ***</span>
  <div>
    *** DIV 3: Following right after span 1 
    <span>*** SPAN 2: INSIDE div 3 ***</span>
    Continue content of div 3 ***
  </div>
</body>
</html>

Here is what this code looks like rendered in the browser:

div and span code example browser screenshot

The visible part of our document starts with a couple of <div> tags (line 8 and 9), which contain the text DIV 1 and DIV 2 accordingly.

The <div> tag is a block-level (or Flow) element, which demands that the browser place it on its own line (by default). This is why we see that the contents of the first two div elements (line 8 DIV 1 and 9 DIV 2) appear on their own line in the screenshot.

Line 10 contains the <span> tag with its content. The <span> tag is an inline (or Phrasing) element and does not get placed on its own line by default. So, why is it shown on its own line in the screenshot? The answer is that the browser has no other choice since the preceeding tag on line 9 is a <div>, which doesn’t allow the tag following it (our <span>) to be placed on the same line. That forces the content of our <span> onto the next line.

Our <span> tag doesn’t demand to take up the entire line like <div> does, so it’s very happy to let the next tag be displayed on the same line. However, it’s the next tag on line 11, which is another <div>, that demands the whole line to itself. That forces the browser to render it on yet another new line.

That’s the long story of how our cute little (undemanding) <span> tag ends up all by itself on a separate line.

(♬ all by myself 😩 ♬)… 😆

You can see the true nature of the <span> tag in lines 11-15. There, it’s sitting inside of another <div> tag along with some other content. Since span is an inline element, it allows the content around it to be on the same line.

You’re Just an Empty Space!

Yikes! That’s harsh! 😆

But not when it comes to how the browser treats the extra empty spaces, new line characters, etc.

Let’s remove all the extra spaces and the new line characters. Here is the (barely readable) code now (div-and-span-no-spaces.html):

1
2
3
4
5
6
7
8
<!doctype html><html><head><meta charset="utf-8"><title>
div and span elements</title></head><body><div>*** DIV 
1: Some content here ***</div><div>*** DIV 2: 
Following right after div 1 ***</div><span>*** SPAN 1:
Following right after div 2 ***</span><div>*** DIV 3:
Following right after span 1 <span>*** SPAN 2: INSIDE div
3 ***</span> Continue content of div 3 ***</div></body>
</html>

(Ok, I did cheat a little and put some new lines here, but that’s just so you don’t have to scroll to see the full code. The actual HTML file, div-and-span-no-spaces.html, doesn’t have these.)

Here is the screenshot of displaying div-and-span-no-spaces.html in the browser:

div and span code with no spaces example browser screenshot

As you can see, nothing changed!

Validation

As the last step, let’s validate our code using one of the online HTML validators, recommended by the WHATWG.

  1. Go to the following link: https://html5.validator.nu/
  2. From the drop down that says Address, choose Text Field
  3. Erase everything in the text area
  4. Copy and paste the HTML code of the div-and-span.html file into that text area and then click the Validate button.

And it’s valid!

div and span code is valid screenshot

Can We Break it?

As mentioned above, an inline element like span is only allowed to contain other inline elements. If I were to place a <div> tag inside of a <span> tag, that would make for an invalid combination.

Let’s do just that in the following code (div-and-span-invalid.html):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>div and span elements</title>
</head>
<body>
  <div>*** DIV 1: Some content here ***</div>
  <div>*** DIV 2: Following right after div 1 ***</div>
  <span>*** SPAN 1: Following right after div 2 ***</span>
  <div>
    *** DIV 3: Following right after span 1 
    <span>*** SPAN 2: INSIDE
        div 3 *** <div>some conetent</div>
    </span>
    Continue content of div 3 ***
  </div>
</body>
</html>

Note that in line 14, I added a <div> tag, sitting right inside of the <span> tag, shown on lines 13-15.

Let’s see what the validator has to say about that!

div and span code is valid screenshot

Invalid! Just as we discussed: Element div is not allowed as a child of element span.... The error message even gives us a parting reminder: Content model for element span: Phrasing content. (As you remember, Phrasing content roughly translates to inline content.)

The “Nothing” Content Model

In the article on Anotomy of an HTML Tag, I mentioned the existence of tags that are not allowed to have any content. In other words, they must only have an opening tag, no content and no closing tag.

An example of a such a tag is the <br> tag, which communicates to the browser to break the text following the <br> tag onto a new line.

The modern HTML spec defines the content model of such elements as “nothing”, also known as void elements. By definition, void elements can never have a closing tag because they can never wrap any content within them.

Which Element Falls into Which Content Model?

In the resources section below, I will provide you with some links you can use to look up which element falls into which modern HTML content model. However, the HTML specification includes a really nice interactive diagram that I referenced above as just a screenshot.

Hovering your mouse over a particular content model bubble shows all of the elements that belong to that content model to the right:

HTML content model animated diagram

Summary

Let’s give a quick summary of what we’ve covered in this article:

  • Content Model refers to the set of rules that define what content each element is allowed to wrap
  • Modern HTML defines 7 types of content models
  • It’s still useful to categorize those 7 content types into the traditional block-level and inline content types
  • Block-level elements begin on a new line by default and are allowed to contain all other elements
  • Inline elements render on the same line and are restricted to containing only other inline elements
  • Traditional block-level content model is roughly the Flow content model in modern HTML
  • Traditional inline content model is roughly the Phrasing content model in modern HTML
  • The modern “nothing” content model, (also known as void elements) are not allowed to wrap any content or have a closing tag

Resources

Questions?

If something is not clear about what I wrote in this article, please ask away in the comments below!