Search code examples
javascripthtmlsvgsetattributesvg-animationelements

Set attribute of basic SVG element with JavaScript


First, below is the desired result without any JavaScript added

* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body { display: flex; justify-content: center; align-items: center; }
svg { width: 12.5rem; height: 6rem; }
path { fill: none; stroke: #444; stroke-width: 5px; }
circle { opacity: 0.75; fill: #0dd; }
<svg>
  <path
    d="M20,50 C20,-50 180,150 180,50 C180-50 20,150 20,50 z" 
  />

  <circle r='10'>
    <animateMotion 
      dur="5s" repeatCount="indefinite"
      path="M20,50 C20,-50 180,150 180,50 C180-50 20,150 20,50 z" 
     />
  </circle>
</svg>

Notice the animateMotion element and values of the path attribute. Everything works perfectly in the snippet above.

In the snippet below, however, we don't have the path attribute yet added on the animateMotion element so we're trying to insert it with JavaScript. In the completed program the path attribute will be filled with dynamic values.

Why doesn't the animateMotion element work here like in the first snippet when the DOM structure is identical?

let animateMotion = document.querySelector( `animateMotion` );

animateMotion.setAttributeNS(
  `http://www.w3.org/2000/svg`, `path`, 
  `M20,50 C20,-50 180,150 180,50 C180-50 20,150 20,50 z`
);
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body { display: flex; justify-content: center; align-items: center; }
svg { width: 12.5rem; height: 6rem; }
path { fill: none; stroke: #444; stroke-width: 5px; }
circle { opacity: 0.75; fill: #0dd; }
<svg>
  <path
    d="M20,50 C20,-50 180,150 180,50 C180-50 20,150 20,50 z" 
  />

  <circle r='10'>
    <animateMotion 
      dur="5s" repeatCount="indefinite"
     />
  </circle>
</svg>

Upon inspection we can see the browser appears to have correctly added the path attribute exactly as it appears in the HTML of the first snippet:

enter image description here


Solution

  • Try passing null as the namespace in setAttributeNS

    let animateMotion = document.querySelector( `animateMotion` );
    
    animateMotion.setAttributeNS(
      null, `path`, 
      `M20,50 C20,-50 180,150 180,50 C180-50 20,150 20,50 z`
    );
    * { box-sizing: border-box; margin: 0; padding: 0; }
    html, body { height: 100%; }
    body { display: flex; justify-content: center; align-items: center; }
    svg { width: 12.5rem; height: 6rem; }
    path { fill: none; stroke: #444; stroke-width: 5px; }
    circle { opacity: 0.75; fill: #0dd; }
    <svg>
      <path
        d="M20,50 C20,-50 180,150 180,50 C180-50 20,150 20,50 z" 
      />
    
      <circle r='10'>
        <animateMotion 
          dur="5s" repeatCount="indefinite"
         />
      </circle>
    </svg>