Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Failed to execute 'appendChild' on 'Node': Only one element on document allowed

Tags:

javascript

svg

I'm trying to load an external svg file and then write additional SVG elements on top of it. When I do, I get the following error

Failed to execute 'appendChild' on 'Node': Only one element on document allowed

when I inspect within Chrome.

Here's the HTML:

<html>
  <head>
    <link rel="stylesheet" type="text/css" href="css/index.css">
    <title>Hello World</title>
  </head>
  <body>
      <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="mySvg">
        <circle cx="40" cy="30" r="15" fill="#FF00FF"
          stroke="#000000" stroke-width="2" />
      </svg>
      <object data="img/drawing.svg" type="image/svg+xml"
        align="center" id="svgDrw" height="80vh"></object>
    <script type="text/javascript" src="js/problem.js"></script>
  </body>
</html>

Here's problem.js:

var svgXtraDoc;
var d = document.getElementById("svgDrw");

d.addEventListener("load",function(){

  // get the inner DOM 
  svgXtraDoc = d.contentDocument;
  // get the inner element by id
  svgRect1 = svgXtraDoc.getElementById("MyRect1");
  svgRect2 = svgXtraDoc.getElementById("MyRect2");
}, false);


function addRect() {
  var svgRect = document.createElementNS("http://www.w3.org/2000/svg", 
    "rect");
  svgRect.setAttribute("x", 100);
  svgRect.setAttribute("y", 100);
  svgRect.setAttribute("width", 100);
  svgRect.setAttribute("height", 100);
  svgRect.style.fill = "red";
  // mySvg.appendChild(svgRect);
  svgXtraDoc.appendChild(svgRect);
}

document.getElementById("mySvg").addEventListener("click", addRect);

... and here's drawing.svg, stripped of some of the Inkscapy cruft:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   width="29.02857mm"
   height="37.092064mm"
   viewBox="0 0 102.85714 131.42857"
   id="svg2"
   version="1.1">
  <g
     id="layer1"
     transform="translate(-205.71428,-218.07649)">
    <rect
       style="stroke:none;fill-opacity:1;fill:#5eb056"
       id="MyRect1"
       width="102.85714"
       height="131.42857"
       x="205.71428"
       y="218.07649" />
    <rect
       style="fill:#000000;stroke:none;fill-opacity:1"
       id="MyRect2"
       width="42.857143"
       height="65.714287"
       x="231.42857"
       y="252.3622" />
  </g>
</svg>

The error arises when you click on the circle: the rectangle that should appear within the two-rectangle imported drawing doesn't appear, and the error message "Failed to execute 'appendChild' on 'Node': Only one element on document allowed." is displayed in the console.

If I change this so the rectangle is added to the svg containing the circle, i.e. by uncommenting the line "// mySvg.appendChild(svgRect);", then the rectangle successfully appears.

I also know that the drawing file is successfully loading, because in other code (not included here for brevity reasons) I can change the colors of the rectangles within that file. So it seems that I can access existing elements within the file but not add elements.

Is there something about externally loaded SVGs versus inline SVGs that I'm not taking into account?

One last thing -- this is going into a Cordova app so I'd like to keep it small, so pure JavaScript solutions are preferred. I'll use JQuery or SnapSVG if I have to but it seems my needs are sufficiently simple that it should be possible to do this without external libraries.

like image 561
William Whyte Avatar asked Dec 23 '16 14:12

William Whyte


2 Answers

In my case, the DOM was complaining because I was trying to add a child in the document itself:

document.appendChild(myElement);

I solved it by getting a reference to the body tag:

document.body.appendChild(logger);
like image 169
Luillyfe Avatar answered Oct 16 '22 10:10

Luillyfe


There is a difference between the root node of a document and the document element. What you want is the <svg> document element, but .contentDocument gets you the root node.

The root node is one hierarchy level further up. This means, the <svg> document element resides inside the root node, and the root node may only have this one <svg> child element.

To get the document element, do this:

svgXtraDoc = d.contentDocument.documentElement;
like image 25
Thomas W Avatar answered Oct 16 '22 10:10

Thomas W