skip to content

How does the HTML parser treat markup inside an inline `<svg>` element differently from ordinary HTML, and what happens if a `<p>` start tag appears inside it?

level: middleimportance: nice to knowfreq 28%

answer

  1. two grammars, one parser
  2. the slash finally does something
  3. names get adjusted, not preserved
  4. certain HTML tags eject you
  5. one legal door back into HTML

basics

~20 s

Inside <svg> the parser switches to foreign-content rules: a trailing slash genuinely self-closes an element, and known attribute names are case-corrected to spellings such as viewBox. A <p> start tag forces a breakout, popping the parser back into HTML.

solid answer

~50 s

An `<svg>` start tag puts tree construction into **foreign content**, where SVG's XML-ish grammar applies instead of HTML's. Three things change. First, the self-closing flag is honoured, so `<circle r="4" />` really closes — which is why copy-pasted SVG parses correctly. Second, names are adjusted: the tokenizer lowercases everything, then the *adjust SVG attributes* step maps known names back to their real spelling, so `viewBox`, `viewbox` and `VIEWBOX` all yield `viewBox` in an HTML document; element names such as `foreignObject`, `clipPath` and `linearGradient` are fixed the same way. Third, a fixed list of HTML start tags — including `p`, `div`, `span`, `table`, `br`, `img`, `ul` and the heading tags — is treated as a **breakout**: the parser reports a parse error, pops elements until it is out of foreign content, and reprocesses the tag as HTML. So the `<p>` becomes an HTML sibling after the SVG, not an SVG child.

code

html · 6 lines
html
<svg viewBox="0 0 200 100">
  <circle cx="50" cy="50" r="40" />
  <foreignObject x="100" y="20" width="90" height="60">
    <p>HTML nested legally.</p>
  </foreignObject>
</svg>

go deeper

for a junior

Know that pasted SVG works inside HTML, that self-closing tags are normal there, and that HTML elements do not belong inside an SVG drawing.

for a middle

Explain foreign content: the self-closing flag is honoured, known element and attribute names are case-adjusted, and a listed HTML start tag pops the parser out of the SVG.

for a senior

Talk about the failure you would actually debug — an SVG sprite or icon component that silently truncates because emitted HTML broke out of foreign content, and how you would confirm it in the built tree.

for a principal

Own the policy: whether SVG is inlined, sprited or referenced, how exported assets are sanitised before they enter templates, and who is accountable for markup that mixes two namespaces.

## Two grammars in one document An HTML document can contain subtrees in two other namespaces: SVG, opened by `<svg>`, and MathML, opened by `<math>`. While the parser is inside one of those subtrees it runs the *rules for parsing tokens in foreign content*, which are deliberately closer to XML than the surrounding HTML rules. Elements created there live in the SVG or MathML namespace, not the HTML namespace. ## The trailing slash finally means something In HTML the self-closing flag on a start-tag token is recorded and then ignored. In foreign content it is acted upon: ```html <svg viewBox="0 0 24 24"> <path d="M4 4h16v16H4z" /> <circle cx="12" cy="12" r="6" /> </svg> ``` Both elements are closed by their slash. This is the whole reason that SVG exported from a drawing tool — written in XML style, with no HTML end tags in sight — can be pasted directly into a page and simply work. ## Case: adjusted, not preserved SVG has case-sensitive names: `viewBox`, `preserveAspectRatio`, `gradientTransform`, `patternUnits`, and elements like `foreignObject`, `clipPath`, `linearGradient`, `textPath`. The HTML tokenizer, however, lowercases every tag name and attribute name it reads — that is unconditional. The fix is in tree construction. On entering foreign content the parser applies *adjust SVG attributes* and an element-name adjustment table, mapping the lowercased forms back to their correct camelCase spelling for a fixed list of known names. The practical result is that in an HTML document all three of these produce an attribute named `viewBox`: ```html <svg viewBox="0 0 10 10"></svg> <svg viewbox="0 0 10 10"></svg> <svg VIEWBOX="0 0 10 10"></svg> ``` Two caveats are worth stating, because they are where people get burned. The adjustment table only covers *known* names, so an invented or misspelled attribute keeps whatever lowercase form it was given. And the repair belongs to the HTML parser: markup built through scripting rather than parsed from HTML source, or a document served as XML, gets no such rescue — there the exact case is what you asked for. ## Breaking out of foreign content Foreign content is permissive about unknown element names — anything that is not recognised is simply inserted in the SVG namespace. But a specific list of HTML start tags acts as an escape hatch, on the assumption that seeing them means the author lost track of an unclosed `</svg>`. The list includes `b`, `big`, `blockquote`, `body`, `br`, `center`, `code`, `dd`, `div`, `dl`, `dt`, `em`, `embed`, `h1`–`h6`, `head`, `hr`, `i`, `img`, `li`, `menu`, `meta`, `nobr`, `ol`, `p`, `pre`, `ruby`, `s`, `small`, `span`, `strong`, `strike`, `sub`, `sup`, `table`, `tt`, `u`, `ul` and `var`. When one of those appears in foreign content, the parser reports a parse error, pops elements off the stack of open elements until it is no longer in foreign content, and then reprocesses the token under normal HTML rules. So: ```html <svg> <circle r="4" /> <p>Caption</p> </svg> ``` does not give you a paragraph inside the drawing. The SVG is closed early and the paragraph becomes an HTML element after it — with the trailing `</svg>` then arriving as a stray end tag. ## The legal way in: integration points HTML *can* live inside an SVG subtree, but only at defined **HTML integration points**: the SVG elements `foreignObject`, `desc` and `title`. Inside those, tokens are processed with the ordinary HTML rules again. ```html <svg viewBox="0 0 200 100"> <foreignObject x="10" y="10" width="180" height="80"> <p>Real HTML, legitimately nested.</p> </foreignObject> </svg> ``` MathML has its own equivalent: `annotation-xml` is an HTML integration point when its `encoding` attribute is `text/html` or `application/xhtml+xml`, and `mtext`, `mi`, `mo`, `mn` and `ms` are MathML text integration points. ## What this is worth in an interview This is not a screening question — it is a depth probe. The valuable parts of the answer are the shape of the model (one parser, two rule sets, defined entry and exit points) and the two consequences authors actually meet: pasted SVG works because `/>` is honoured, and dropping HTML into an SVG silently ends the SVG rather than nesting inside it.

  • How do you legitimately put HTML inside an SVG subtree?
    Use an HTML integration point. In SVG those are `foreignObject`, `desc` and `title`: inside them the parser returns to ordinary HTML rules, so paragraphs, lists and links nest normally. Anywhere else in the SVG, an HTML start tag from the breakout list ends the foreign content instead of nesting, which is why `foreignObject` exists at all.
  • Does the same foreign-content handling apply to MathML?
    Yes. A `<math>` start tag enters foreign content with an *adjust MathML attributes* step of its own. MathML's HTML integration point is `annotation-xml` when its `encoding` attribute is `text/html` or `application/xhtml+xml`, and `mtext`, `mi`, `mo`, `mn` and `ms` are text integration points where HTML tokens are processed normally.
  • If lowercase `viewbox` works in an HTML file, when does SVG attribute case actually matter?
    Whenever the HTML parser is not the thing creating the attribute. Markup built through scripting APIs, or a document served and parsed as XML, gets no name adjustment, so the exact spelling you supply is the name you get. The parser's adjustment table is a repair for HTML source only, and it covers known names, not misspelled ones.

saying these in an interview costs you the question

  • Says lowercase viewbox breaks the SVG in an HTML file
  • Claims inline SVG must be XML-well-formed to parse at all
  • Thinks a div inside svg nests as an SVG child
  • Believes HTML can never appear inside an SVG subtree
  • Assumes the slash self-closes HTML elements too

context