Skip to content

Parse raw html against the element it is inserted into, so svg keeps its namespace - #893

Merged
schultek merged 1 commit into
schultek:mainfrom
diegolopezrm:fix/raw-text-svg-namespace
Sep 24, 2026
Merged

schultek merged 1 commit into
schultek:mainfrom
diegolopezrm:fix/raw-text-svg-namespace

Conversation

@diegolopezrm

Copy link
Copy Markdown
Contributor

Description

raw() put its nodes in the xhtml namespace, so svg markup rendered as nothing.

The markup was parsed by assigning innerHTML on a <template>, where the html parser has no element to parse against. <circle> therefore came out as an unknown html element rather than an svg one — present in the dom, laid out as nothing. Server-rendered output showed the shape, and it disappeared the moment the component hydrated, which is what #567 reports.

It is now parsed with Range.createContextualFragment against the nearest ancestor element, so the nodes come out in that element's namespace. The element is already reachable from the component's element through parentRenderObjectElement; the chain is walked until an element turns up, because a fragment renders into a DocumentFragment. The template path is kept for when there is no element to parse against, which is a fragment at the root of a component tree.

Parsing against an element also settles markup that is only valid inside a specific parent, such as a <td> in a <tr>.

raw_text_web.dart is identical on next, so this applies there unchanged.

Type of Change

  • 🛠️ Bug fix

Tests

packages/jaspr/test/dom/html/raw_text_browser_test.dart, five cases:

  • html is unchanged and stays in the xhtml namespace
  • an svg fragment (<circle>) comes out in the svg namespace
  • a nested fragment (<g> with a child) keeps the namespace all the way down
  • hydrating over the server's markup leaves one svg circle, not a broken replacement
  • a full <svg> element inside html still works, which the html parser already handled — there to catch a fix that special-cases the namespace too eagerly

The two svg fragment cases and the hydration case fail before the change and pass after. dart test -p chrome and dart test -p vm are green in packages/jaspr.

Fixes #567

`RawText` assigned `innerHTML` on a `<template>`, so the html parser had no
element to parse against and put every node in the xhtml namespace. Inside an
`<svg>` that made `<circle>` an unknown html element, which the browser lays
out as nothing: the server-rendered markup showed the shape and it vanished as
soon as the component hydrated.

The markup is now parsed with `Range.createContextualFragment` against the
nearest ancestor element, so the nodes come out in that element's namespace.
That also covers markup that is only valid inside a specific parent, such as a
`<td>` in a `<tr>`. The template is kept for when there is no element to parse
against, which is what a fragment at the root of a component tree is.

Fixes schultek#567
@diegolopezrm
diegolopezrm force-pushed the fix/raw-text-svg-namespace branch from 1b0000a to 8c3fd0b Compare September 22, 2026 17:59
@github-actions

Copy link
Copy Markdown

Package Version Report

The following packages have been updated:
jaspr_test : 0.23.4 -> 0.23.5
jaspr : 0.23.4 -> 0.23.5
jaspr_builder : 0.23.4 -> 0.23.5
jaspr_cli : 0.23.4 -> 0.23.5

@schultek schultek left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I didn't know about the Range.createContextualFragment API, this is great!

@schultek
schultek merged commit 07b58ee into schultek:main Sep 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: svg elements do not render when created with RawText

2 participants