A proposal for improving the SVG DOM

Cameron McCormack (revisions)

This is a strawman proposal for a new SVG DOM that is easier to use and more consistent with HTML. Since the existing SVG DOM uses convenient names that we cannot extend with the simpler behaviour, we allow the author to opt in to it and use the opportunity to simplify the use of namespaces in SVG.

The changes here are described relative to SVG 1.1. New elements and features from SVG 2 which have not been implemented yet, such as hatchPath, would be redesigned to match the new design.


  1. 1. Opting in
  2. 2. New root element
  3. 3. New viewport-establishing element
  4. 4. Renaming of elements and attributes for case consistency
  5. 5. Unification of common elements
  6. 6. Mixing of SVG- and HTML-namespaced SVG content
  7. 7. Interface hierarchy
  8. 8. Reflecting attributes
    1. 8.1 Strings
    2. 8.2 Booleans
    3. 8.3 Enumerated values
    4. 8.4 Integers
    5. 8.5 Numbers
    6. 8.6 Lengths
    7. 8.7 Angles
    8. 8.8 Length lists
    9. 8.9 Number lists
    10. 8.10 Transform lists
    11. 8.11 Path data
    12. 8.12 Points
    13. 8.13 preserveAspectRatio
    14. 8.14 Animated attribute value access
  9. 9. Media type

1. Opting in

To opt in to the new SVG DOM for a particular element, authors must create the relevant SVG element either in the HTML namespace or in no namespace. Thus, when creating an element from script, the author would write:

var rect = document.createElementNS("http://www.w3.org/1999/xhtml", "rect");


var rect = document.createElementNS(null, "rect");

or simply:

var rect = document.createElement("rect");

Note that in an HTML document, the createElement call will place the element in the HTML namespace, and in an XML document, it will use no namespace. Using createElement will be the recommended way to create elements from script.

2. New root element

We define a new root element for SVG, graphics, for SVG documents that use the new DOM. This element behaves much the same as an outer svg element, although it omits attributes which make sense for inner svg elements but not outer ones, such as x and y.

The use of a new root element allows the HTML parser to use a mode that continues to place elements within the HTML namespace, rather than the SVG namespace.


A new HTML parsing mode is probably still required, since we would want to ensure <image> is treated as an SVG image element. Would be good to avoid if we could, though.


Can we use this opportunity to have the HTML parser support simpler inclusion of HTML content in SVG? For example:

<!DOCTYPE html>
<p>Check out this diagram:</p>
<graphics width="200" height="200">
  <path d="..."/>
  <div x="50" y="50" width="100" height="100">
    <p>... <span> ... </span> ... </p>

Should we be considering the reverse at the same time, where non-root SVG elements are included directly in HTML?


Having a root graphics element in a different namespace makes it impossible for newly authored SVG documents (and SVG in HTML fragments) to be backwards compatible with user agents that only understand SVG as it exists now. Is it possible to design the opt in so that old user agents still parse and render documents that use the new SVG DOM, even though any scripting may not work? Would this be beneficial anyway?

We could provide authors with a script library that converts graphics elements to svg elements and renames their attributes to have the correct casing and values. The library could even replace the old APIs with new ones, assuming they follow Web IDL’s requirements for reflecting IDL attributes as configurable properties. (This would work for SVG-in-HTML, but not for a document with a root SVG element, where processing a document with an unknown root graphics element won’t run the script. It also wouldn’t work for SVG as an image, although there would be no need to patch the new API in.)

3. New viewport-establishing element

Having replaced the outer svg element with with the graphics element, it makes less sense to keep an element named svg as being the element that establishes a viewport in the middle of an SVG document. So we introduce a new element named viewport for this purpose.

4. Renaming of elements and attributes for case consistency

To align with the naming of elements and attributes in HTML, the SVG elements that are defined in the HTML namespace (or no namespace) have purely lowercase names for themselves and their attributes.

Old element nameNew element name
feBlend, etc.feblend, etc.

(It is likely that we will drop altGlyph, altGlyphDef, altGlyphItem and glyphRef in favour of a better designed glyph selection and rendering mechanism in SVG 2, so it is not included here. Also, animateColor is omitted since it has no benefit over animate.)

Old attribute nameNew attribute name

(baseProfile is dropped.)

Elements that are mixed case in SVG 1.1 will not be recognised in all lowercase in the SVG namespace. Attributes with mixed case will not be recognised on the newly namespaced elements.


While we’re fixing the case of attributes, is it in scope to rename some of them? Specifically I’m thinking about animation where SVG has repeatCount and CSS has ‘animation-iteration-count˚. It's tempting to rename repeatCount to iterationcount while we’re at it. There are probably many similar cases.

Similarly, since we’re promoting some of these attributes to properties, should we rename some of them as properties? For example, limitingConeAnglelimiting-cone-angle?

5. Unification of common elements

The a, script and style are common to both SVG and HTML, but with slightly different behaviour and interfaces. Since the SVG elements now will live in the HTML namespace (or in no namespace), they need to be unified. For the script and style elements, there are no attributes or IDL interface members from SVG that need to be preserved. However, the a element is classed as a container element, and has these SVG specific attributes:

and these interface members that don't correspond to anything on HTMLAnchorElement:

/* from SVGTransformable */
  readonly attribute SVGAnimatedTransformList transform;

/* from SVGLocatable */
  readonly attribute SVGElement? nearestViewportElement;
  readonly attribute SVGElement? farthestViewportElement;

  SVGRect getBBox();
  SVGMatrix? getCTM();
  SVGMatrix? getScreenCTM();
  SVGMatrix getTransformToElement(SVGGraphicsElement element);

/* from SVGTests */
  readonly attribute SVGStringList requiredFeatures;
  readonly attribute SVGStringList requiredExtensions;
  readonly attribute SVGStringList systemLanguage;

  boolean hasExtension(DOMString extension);

We can move these interface members to an interface that can be mixed in to HTMLAnchorElement (and SVGTests is already such an interface).


What about title?

6. Mixing of SVG- and HTML-namespaced SVG content

Implementations would be required to keep supporting the SVG-namespaced elements from SVG 1.1. This means SVG- and HTML-namespaced content could be mixed. Authoring conformance requirements would disallow this, but implementations would need to support, for example, an SVG-namespaced g element as a child of an HTML-namespaced graphics element.

7. Interface hierarchy

The interfaces for the new SVG DOM cannot coexist with those from the SVG 1.1 DOM if they keep the same names. So the new interfaces take on the naming scheme SVG2FooElement.

However, as mentioned earlier, the interfaces for a, script and style would be HTMLAnchorElement, HTMLScriptElement and HTMLStyleElement.

Instead of having SVG2Element inherit from Element, we have it inherit from HTMLElement. This means SVG elements gain the ability to use APIs such as tabIndex, dataset, contentEditable, and so on.


Alternative naming schemes are HTMLSVGFooElement or even HTMLFooElement. (Although HTMLFooElement wouldn't work for the image element.)

We could actually re-use the SVG 1.1 interfaces, but this would require heavily using union types. For example:

interface SVGRectElement : ... {
  attribute (SVGAnimatedLength or float or DOMString) x;

given the changes to how attributes are reflected, described in the next section. Prose then would need to define that assigning to x when the object is in the SVG namespace (i.e., is using the old SVG DOM) would throw an exception.

The only thing that can't be worked around is the new inheritance for SVGElement. It might be acceptable just to do that for both the old and new SVG DOMs.

We would rename the SVGGraphicsElement interface that is currently in SVG 2 to SVG2GraphicElement, since SVG2GraphicsElement would be the interface for the graphics element.


Would new APIs we come up with be available on the old interfaces and the new ones? Similarly, would new elements be only usable in the HTML (or no) namespace, or would they work in the SVG namespace too? If they did work in the SVG namespace, would there be a corresponding “old” interface for it?

8. Reflecting attributes

One of the most awkard aspects of the existing SVG DOM is how content attributes are reflected. In the new SVG DOM, attributes are reflected ignoring the effect of animations. They are also reflected using more useful data types, avoiding the need for the author to navigate a chain of objects.

8.1 Strings

Instead of SVGAnimatedString, string-valued attributes are reflected as a simple DOMString-typed IDL attribute.

For example, instead of:

interface SVGAnimatedString {
           attribute DOMString baseVal;
  readonly attribute DOMString animVal;

interface SVGURIReference {
  readonly attribute SVGAnimatedString href;

interface SVGTextPathElement : ... {
SVGTextPathElement implements SVGURIReference;

we have:

interface SVG2TextPathElement : ... {
  attribute DOMString href;

8.2 Booleans

Instead of SVGAnimatedBoolean, boolean-valued attributes are reflected as a simple boolean-typed IDL attribute.

For example, instead of:

interface SVGAnimatedBoolean {
           attribute boolean baseVal;
  readonly attribute boolean animVal;

interface SVGFEConvolveMatrixElement : ... {
  readonly attribute SVGAnimatedBoolean preserveAlpha;

we have:

interface SVG2FEConvolveMatrixElement : ... {
  attribute boolean preserveAlpha;

8.3 Enumerated values

Instead of SVGAnimatedEnumeration, enumerated value attributes are reflected as an IDL attribute whose type is either a DOMString or an enum, depending on how complex the values are.

For example, instead of:

interface SVGAnimatedEnumeration {
           attribute unsigned short baseVal;
  readonly attribute unsigned short animVal;

interface SVGTextContentElement : ... {
  const unsigned short LENGTHADJUST_UNKNOWN = 0;
  const unsigned short LENGTHADJUST_SPACING = 1;
  const unsigned short LENGTHADJUST_SPACINGANDGLYPHS = 2;

  readonly attribute SVGAnimatedEnumeration lengthAdjust;

we have:

enum LengthAdjustType {
  "spacing", "spacingAndGlyphs"

interface SVG2TextContentElement : ... {
  attribute LengthAdjustType lengthAdjust;

8.4 Integers

Instead of SVGAnimatedInteger, integer-valued attributes are reflected as a simple long-typed IDL attribute.

For example, instead of:

interface SVGAnimatedInteger {
           attribute long baseVal;
  readonly attribute long animVal;

interface SVGFEConvolveMatrixElement : ... {
  readonly attribute SVGAnimatedInteger orderX;

we have:

interface SVG2FEConvolveMatrixElement : ... {
  attribute long orderX;

8.5 Numbers

Instead of SVGAnimatedNumber, integer-valued attributes are reflected as a simple float-typed IDL attribute.

For example, instead of:

interface SVGAnimatedNumber {
           attribute float baseVal;
  readonly attribute float animVal;

interface SVGPathElement : ... {
  readonly attribute SVGAnimatedNumber pathLength;

we have:

interface SVG2PathElement : ... {
  attribute float pathLength;

8.6 Lengths

Instead of SVGAnimatedLength, length-valued attributes are reflected as an IDL attribute of type (float or DOMString) that on getting always returns a float representing the length in user units. On setting, the DOMString value can be any valid CSS length.

For example, instead of:

interface SVGAnimatedLength {
  readonly attribute SVGLength baseVal;
  readonly attribute SVGLength animVal;

interface SVGRectElement : ... {
  readonly attribute SVGAnimatedLength x;

we have:

interface SVG2RectElement : ... {
  attribute (float or DOMString) x;

This allows for setting a length string but not getting it back. For example:

rectElement.x = "1em";
alert([typeof rectElement.x, rectElement.x]);  // alerts "number, 16", for example

Is this too confusing?

You can still do getAttribute("x") to get at the string value. Do we need a DOMString accessor or an SVGLength accessor? CSSOM still hasn't grown an object to represent CSS lengths, either.

If we were to provide an SVGLength (or future CSSLength) accessor, we could do it with:

  attribute SVGLength xAsLength;

although the naming there is not the best.

8.7 Angles

The only use of SVGAnimatedAngle currently is for orient on marker, which takes either an angle value or an enumerated string value. So instead of:

interface SVGAnimatedAngle {
  readonly attribute SVGAngle baseVal;
  readonly attribute SVGAngle animVal;

interface SVGMarkerElement : ... {
  readonly attribute SVGAnimatedEnumeration orientType;
  readonly attribute SVGAnimatedAngle orientAngle;

we would have:

interface SVG2MarkerElement : ... {
  attribute (float or DOMString) orient;

where on setting with a string, any valid CSS angle or orient keyword (such as “auto”) can be used, and on getting, the keyword is returned if that is set, and a number representing the angle in degrees otherwise.

8.8 Length lists

The only uses of SVGAnimatedLengthList are for the x, y, dx and dy attributes on text positioning elements. Since we shouldn't be promoting the use of length lists for glyph positioning, and instead be introducing a feature that supports this by addressing glyphs rather than characters, we don't provide SVG DOM access to these length lists. Instead, we reflect the values as single lengths.

So, intead of:

interface SVGAnimatedLengthList {
  readonly attribute SVGLengthList baseVal;
  readonly attribute SVGLengthList animVal;

interface SVGTextPositioningElement : ... {
  readonly attribute SVGAnimatedLengthList x;

we have:

interface SVG2TextPositioningElement : ... {
  attribute (float or DOMString) x;

Assigning a string will cause it to be parsed as a single CSS length value, and if the current attribute value has more than one value, the entire list is replaced with the new single value. Getting the attribute returns the value of the first element of the length list.


If we wanted to, we could add some methods to get and set the attribute value as a list, like:

  sequence<float> getXPositions();
  void setXPositions(sequence<float> xs);

It's probably not worth doing, though.

8.9 Number lists

Instead of representing attributes with a list of numbers as an SVGNumberList, they are reflected as a DOMString-typed attribute and a pair of methods to get and set the attribute as an array of numbers.

So instead of:

interface SVGAnimatedNumberList {
  readonly attribute SVGNumberList baseVal;
  readonly attribute SVGNumberList animVal;

interface SVGComponentTransferFunctionElement : ... {
  readonly attribute SVGAnimatedNumberList tableValues;

we have:

interface SVG2ComponentTransferFunctionElement : ... {
  attribute DOMString tableValues;
  sequence<float> getTableValues();
  void setTableValues(sequence<float> tableValues);

The pattern of using sequences with get/set methods seems a little awkward in some cases, e.g. I think the current API looks like this:


while the new API looks like:


(That said, Web Animations follows this pattern for getting and setting keyframes.)

8.10 Transform lists

Instead of SVGAnimatedTransformList, attributes that take a transform list are reflected as a DOMString-valued IDL attribute and a pair of methods to get and set the list of transform items.

For example, instead of:

interface SVGAnimatedTransformList {
  readonly attribute SVGTransformList baseVal;
  readonly attribute SVGTransformList animVal;

interface SVGPatternElement : ... {
  readonly attribute SVGAnimatedTranformList patternTransform;

we have:

interface SVG2PatternElement : ... {
  attribute DOMString patternTransform;
  sequence<SVGTransform> getPatternTransformItems();
  void setPatternTransformItems(sequence<SVGTransform> patternTransform);

Should we keep using SVGTransform to represent each transform item? It might be simpler to use dictionaries. For example:

enum TransformType {
  "matrix", "translate", "scale", "rotate", "skewX", "skewY"

dictionary TransformItem {
  TransformType type;

dictionary ScaleTransformItem : TransformItem {
  float scaleX;
  float scaleY;

dictionary TranslateTransformItem : TransformItem {
  float translateX;
  float translateY;


interface SVG2PatternElement : ... {
  attribute DOMString patternTransform;
  sequence<TransformItem> getPatternTransformItems();
  void setPatternTransformItems(sequence<TransformItem> patternTransform);

Web IDL currently doesn't support using a superclass dictionary in the IDL and treating it as a more derived dictionary in prose, but we could make that work.

8.11 Path data

Instead of SVGAnimatedPathData and its parallel base and animated SVGPathSegLists, attributes that contain path data are reflected as a DOMString-valued IDL attribute and a pair of methods to get and set the list of path commands.

For example, instead of:

interface SVGAnimatedPathData {
  readonly attribute SVGPathSegList pathSegList;
  readonly attribute SVGPathSegList normalizedPathSegList;
  readonly attribute SVGPathSegList animatedPathSegList;
  readonly attribute SVGPathSegList animatedNormalizedPathSegList;

interface SVGPathElement : ... {
SVGPathElement implements SVGAnimatedPathData;

we have:

interface SVG2PathElement : ... {
  attribute DOMString d;
  sequence<SVGPathSeg> getPathSegments();
  void setPathSegments(sequence<SVGPathSeg> pathSegments);

There is an even stronger case for moving away from SVGPathSeg and using dictionaries instead, given the multitude of SVGPathSeg sub-interfaces. For example we could do:

enum PathSegmentType {
  "move", "close", "lineto", "horizontal-lineto", "vertical-lineto",
  "curveto", "smooth-curveto", "quadratic-curveto", "smooth-quadratic-curveto",

enum PathSegmentLetter {
  "M", "m", "z", "L", "l", "H", "h", "V", "v", "C", "c", "S", "s", "Q", "q",
  "T", "t", "A", "a"

dictionary PathSegment {
  PathSegmentType type;
  PathSegmentLetter letter;
  boolean relative;

dictionary OnePointPathSegment : PathSegment {
  float x;
  float y;

dictionary TwoPointPathSegment : OnePointPathSegment {
  float x1;
  float y1;

dictionary ThreePointPathSegment : TwoPointPathSegment {
  float x2;
  float y2;

dictionary ArcPathSegment : OnePointTransformItem {
  float r1;
  float r2;
  float angle;
  boolean largeArcFlag;
  boolean sweepFlag;

interface SVG2PathElement : ... {
  attribute DOMString d;
  sequence<PathSegment> getPathSegments();
  void setPathSegments(sequence<PathSegment> pathSegments);

Instead of maintaining a separate, live normalised path segment list, we could have a function that can take a sequence<SVGPathSeg> or sequence<PathSegment> and convert it into the equivalent normalised segment list.


Are there other places that use SVGPathSegList? (Web Animations does but that’s easily fixed). It seems slightly useful to be able to have a type representing a parsed SVG path, but perhaps that could simply be a typedef of sequence<PathSegment>.


Removing the interfaces and providing only non-live dictionaries makes it difficult for applications that want to work with the live state of some data.

For example, it’s possible to animate a path and then use that animated path as a motion path. With Web Animations we can represent that by simply pointing to the animated SVGPathSegList. Since the data type is live, we automatically reflect any changes to it including animations.

While we could add a feature that points to a specific SVG path element and internally watch for changes to its path, that would bind us to a specific kind of element and prevent reuse with other possible providers of paths. Also, it doesn’t help script libraries that want to provide this sort of feature (such as the polyfill).

I wonder if there are other instances of this where libraries depend on having a live copy of a list of some sort. For infrequent changes you can manage them with mutation observers but for animated data that doesn’t work. Also, fetching a new sequence on every sample sounds expensive, but maybe it’s not.

8.12 Points

Instead of SVGAnimatedPoints and its parallel base and animated SVGPointLists, attributes that contain point lists are reflected as a DOMString-valued IDL attribute and a pair of methods to get and set the list of points.

For example, instead of:

interface SVGAnimatedPoints {
  readonly attribute SVGPointList points;
  readonly attribute SVGPointList animatedPoints;

interface SVGPolygonElement : ... {
SVGPolygonElement implements SVGAnimatedPoints;

we would have:

interface SVG2PolygonElement : ... {
  attribute DOMString points;
  sequence<SVGPoint> getPoints();
  void setPoints(sequence<SVGPoint> points);

Again we could use dictionaries instead of SVGPoint. We lose the matrixTransform method, but an equivalent one could be put on SVGMatrix or Matrix.

dictionary Point {
  float x;
  float y;

interface SVG2PolygonElement : ... {
  attribute DOMString points;
  sequence<Point> getPoints();
  void setPoints(sequence<Point> points);

8.13 preserveAspectRatio

As the behaviour of the preserveAspectRatio attribute can be entirely described in terms of the ‘object-fit’ and ‘object-position’ properties, we remove the typed SVG DOM accesors for it.

Instead of:

interface SVGPreserveAspectRatio {

  // Alignment Types
  const unsigned short SVG_PRESERVEASPECTRATIO_UNKNOWN = 0;
  const unsigned short SVG_PRESERVEASPECTRATIO_NONE = 1;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMINYMIN = 2;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMIDYMIN = 3;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMAXYMIN = 4;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMINYMID = 5;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMIDYMID = 6;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMAXYMID = 7;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMINYMAX = 8;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMIDYMAX = 9;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMAXYMAX = 10;

  // Meet-or-slice Types
  const unsigned short SVG_MEETORSLICE_UNKNOWN = 0;
  const unsigned short SVG_MEETORSLICE_MEET = 1;
  const unsigned short SVG_MEETORSLICE_SLICE = 2;

  attribute unsigned short align;
  attribute unsigned short meetOrSlice;

interface SVGAnimatedPreserveAspectRatio {
  readonly attribute SVGPreserveAspectRatio baseVal;
  readonly attribute SVGPreserveAspectRatio animVal;

interface SVGSVGElement : ... {
  readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;

we have:

interface SVG2ViewportElement : ... {
  attribute DOMString preserveAspectRatio;

The attribute becomes a presentation attribute that maps to ‘object-fit’ and ‘object-position’, and authors can use getComputedStyle to get at those values.

8.14 Animated attribute value access




So long as we’re looking at this, we probably should find a scheme that works for HTML too. Currently I have no intention of making arbitrary HTML attributes animatable, but that request is sure to arrive at some point (particularly in combination with set).

Thinking out loud,

  1. We could just provide a variant of getAttribute:
  2. We could try and mimick getComputedStyle somewhat:
    window.getAnimatedValue(elem, "href") ?
  3. We could even return an animated variant of the Element:
  4. We could give up on providing the means to query the animated values of attributes since many of the ones you want to animate have been promoted to properties already and can be queried with getComputedStyle. (That obviously wouldn’t be great if indeed more people want to animate SVG/HTML attributes, and it’s pretty unfortunate if you can animate href and there’s no way to query that.)

There is Tab’s proposal for Cascading Attribute Sheets but it looks like that just clobbers underlying values currently so it doesn’t require a special method for querying the underlying vs stylesheet value. However, I think UAs will need to unapply CAS transformations when an object is removed from the document and therefore will need to track these separately in which case such a method might be useful.

In terms of Web Animations, it’s currently all expressed in terms of properties. The current thinking is that handling of attributes will be a special feature of the SVG bindings (since obviously CSS doesn’t need it).

However, the animation model for attributes should be the same as for properties–both use the concept of a base value which is added to or replaced on each sample. Since the API you proposed is all in terms of the base value this is fine. The only thing that remains is to add getters for the animated value. I think exposing DOMStrings only would be fine for that.

9. Removed APIs

We can remove a number of interface members in the new SVG DOM. These include:

10. Media type

For standalone SVG documents written in XML, the media type remains image/svg+xml.