skip to Main Content

I am foraying back into the world of web development, so there is a good chance that this is something silly I have overlooked.

I have been able to dynamically create all the HTML components for the SVG I am trying to create, however the resulting image isn’t displayed in it’s container. My aim is to create an SVG element with a random number of circles, this needs to be repeatable so that I can display more than one on the page at a time.

This is an example of what my code outputs:

<svg class="canvas" id="gen1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
 <filter id="shadow">
   <feDropShadow dx="0" dy="10" stdDeviation="2" flood-opacity="0.4"></feDropShadow> 
 </filter>
 <radialGradient id="highlight" cx="50%" cy="50%" r="50%" fx="50%" fy="25%">
  <stop offset="0%" stop-color="#FF4D4D"></stop>
  <stop offset="100%" stop-color="#8B0000"></stop>
 </radialGradient>
 <circle id="circle1" cx="500" cy="166.66666666666666" r="83.33333333333333" fill="url(#highlight)" filter="url(#shadow)"></circle>
 <circle id="circle2" cx="500" cy="666.6666666666666" r="83.33333333333333" fill="url(#highlight)" filter="url(#shadow)"></circle>
 <circle id="circle3" cx="166.6666666666669" cy="744.0169358562925" r="83.33333333333333" fill="url(#highlight)" filter="url(#shadow)"></circle>
 <circle id="circle4" cx="750.0000000000001" cy="599.6793685588859" r="83.33333333333333" fill="url(#highlight)" filter="url(#shadow)"></circle>
</svg>

If I take the above code and paste it into an online SVG viewer the output is exactly what I expect it to be. However inside the basic page I have built it doesn’t show any of the circles, even though when I inspect the HTML I can see all these elements there.

This is the code I am using to generate the SVG:

let canvases = [
    {
        number: 1,
        target: 9,
        circles: [{
            id: 1,
            x: 500,
            y: 166.66666666666666,
            color: "#33FFD4"
        },
        {
            id: 2,
            x: 500,
            y: 666.6666666666666,
            color: "#33FFD4"
        }]
    },
    {
        number: 2,
        target: 5,
        circles: [{
            id: 1,
            x: 166.6666666666669,
            y: 744.0169358562925,
            color: "#33FFD4"
        },
        {
            id: 2,
            x: 750.0000000000001,
            y: 599.6793685588859,
            color: "#33FFD4"
        }]
    }
];

class Node {
    constructor(type = "div", attributes = [], content = null, childNodes = null) {
        this.type = type;
        this.attributes = attributes;
        this.content = content;
        this.childNodes = childNodes;
    }
}

class Attribute {
    constructor(type = "class", value = null) {
        this.type = type;
        this.value = value;
    }
}

function buildCanvas(canvasNumber) {
    let canvas = buildNode(new Node("div", [new Attribute("class", "canvas"), new Attribute("id", "gen" + canvasNumber)], undefined, [
        new Node("h1", [new Attribute("class", "canvasTitle"), new Attribute("id", "canvas" + canvasNumber + "Title")], "Canvas " + canvasNumber),
        new Node("svg", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("class", "canvas"), new Attribute("id", "canvas" + canvasNumber + "Layout"), new Attribute("xmlns", "http://www.w3.org/2000/svg"), new Attribute("viewBox", "0 0 1000 1000")], undefined,  [
            new Node("filter", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("id", "shadow")], undefined, [
                new Node("feDropShadow", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("dx", "0"), new Attribute("dy", "10"), new Attribute("stdDeviation", "2"), new Attribute("flood-opacity", "0.4")])
            ]),
            new Node("radialGradient",  [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("id", "highlight"), new Attribute("cx", "50%"), new Attribute("cy", "50%"), new Attribute("r", "50%"), new Attribute("fx", "50%"), new Attribute("fy", "25%")], undefined, [
                new Node("stop", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("offset", "0%"), new Attribute("stop-color", "#FF4D4D")]),
                new Node("stop", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("offset", "100%"), new Attribute("stop-color", "#8B0000")])
            ])
        ].concat(drawHole(canvases[canvasNumber - 1]))),
        new Node("h3", [new Attribute("class", "canvasTarget"), new Attribute("id", "canvas" + canvasNumber + "Target")], "Target " + canvases[canvasNumber - 1].target),
        new Node("div", [new Attribute("class", "canvasScores"), new Attribute("id", "canvas" + canvasNumber + "Scores")])
    ]));
    return canvas;
}

function drawHole(canvas) {
    const circlePositions = canvas.circles;
    let circleNum = 1;
    let circles = [];
    circlePositions.forEach(circle => {
        circles.push(new Node("circle", [
            new Attribute("ns", "http://www.w3.org/2000/svg"),
            new Attribute("id", "circle" + circleNum),
            new Attribute("cx", circle.x),
            new Attribute("cy", circle.y),
            new Attribute("r", 1000/12),
            new Attribute("fill", "url(#highlight)"),
            new Attribute("filter", "url(#shadow)")]))
        circleNum++;
    });

    return circles;
}

function buildNode(node = new Node("div")) {
    let nodeComp = document.createElement(node.type);
    if (node.attributes != null && typeof node.attributes !== "undefined" && node.attributes.length > 0) {
        let ns;
        for (let position = 0; position < node.attributes.length; position++) {
            if (node.attributes[position].type == "ns") {
                ns = node.attributes[position].value;
                nodeComp = document.createElementNS(ns, node.type);
                continue;
            }
            if (ns != null && typeof ns !== "undefined") {
                try {
                    nodeComp.setAttributeNS(ns, node.attributes[position].type, node.attributes[position].value);
                    continue;
                }
                catch (err) {

                }
            }
            let attribute = document.createAttribute(node.attributes[position].type);
            attribute.value = node.attributes[position].value;
            nodeComp.setAttributeNode(attribute);
        }
    }
    if (node.content != null && typeof node.content !== "undefined") {
        nodeComp.innerHTML = node.content;
    }
    if (node.childNodes != null && typeof node.childNodes !== "undefined" && node.childNodes.length > 0) {
        for (let childNode = 0; childNode < node.childNodes.length; childNode++) {
            nodeComp.insertAdjacentElement("beforeend", buildNode(node.childNodes[childNode]));
        }
    }
    return nodeComp;
}

document.getElementById("canvas-container").insertAdjacentElement("beforeend", buildCanvas(1));
<div id="canvas-container"></div>

If anyone can spot my mistake or niggle to make the circles display on the canvas please put me out of my misery.

2

Answers


  1. You can use the node interface but it’s very verbose. Maybe you ran into trouble creating a svg element dynamically, I think you have to use createElementNS.

    A simpler approach is just to write html out.

    function buildCanvas() {
      const div = document.querySelector("#svg_goes_here");
      
      div.innerHTML = `<svg class="canvas" id="gen1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
     <filter id="shadow">
       <feDropShadow dx="0" dy="10" stdDeviation="2" flood-opacity="0.4"></feDropShadow> 
     </filter>
     <radialGradient id="highlight" cx="50%" cy="50%" r="50%" fx="50%" fy="25%">
      <stop offset="0%" stop-color="#FF4D4D"></stop>
      <stop offset="100%" stop-color="#8B0000"></stop>
     </radialGradient></svg>`;
     
     const svg = div.querySelector("svg");
     
     const ballPositions = [
      {x: 100, y: 100},
      {x: 300, y: 200}
     ];
     
     ballPositions.forEach(ball => {
       let circle = `<circle cx='${ball.x}' cy='${ball.y}' r='${1000/12}' fill='url(#highlight)' filter='url(#shadow)'></circle>`;
       svg.innerHTML += circle;
     });
    }
    
    buildCanvas();
    <div id='svg_goes_here'></div>
    Login or Signup to reply.
  2. While SVG elements reside in the SVG namespace, their attributes typically do not i.e. you need to use setAttribute to create most SVG attributes.

    let canvases = [
        {
            number: 1,
            target: 9,
            circles: [{
                id: 1,
                x: 500,
                y: 166.66666666666666,
                color: "#33FFD4"
            },
            {
                id: 2,
                x: 500,
                y: 666.6666666666666,
                color: "#33FFD4"
            }]
        },
        {
            number: 2,
            target: 5,
            circles: [{
                id: 1,
                x: 166.6666666666669,
                y: 744.0169358562925,
                color: "#33FFD4"
            },
            {
                id: 2,
                x: 750.0000000000001,
                y: 599.6793685588859,
                color: "#33FFD4"
            }]
        }
    ];
    
    class Node {
        constructor(type = "div", attributes = [], content = null, childNodes = null) {
            this.type = type;
            this.attributes = attributes;
            this.content = content;
            this.childNodes = childNodes;
        }
    }
    
    class Attribute {
        constructor(type = "class", value = null) {
            this.type = type;
            this.value = value;
        }
    }
    
    function buildCanvas(canvasNumber) {
        let canvas = buildNode(new Node("div", [new Attribute("class", "canvas"), new Attribute("id", "gen" + canvasNumber)], undefined, [
            new Node("h1", [new Attribute("class", "canvasTitle"), new Attribute("id", "canvas" + canvasNumber + "Title")], "Canvas " + canvasNumber),
            new Node("svg", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("class", "canvas"), new Attribute("id", "canvas" + canvasNumber + "Layout"), new Attribute("xmlns", "http://www.w3.org/2000/svg"), new Attribute("viewBox", "0 0 1000 1000")], undefined,  [
                new Node("filter", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("id", "shadow")], undefined, [
                    new Node("feDropShadow", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("dx", "0"), new Attribute("dy", "10"), new Attribute("stdDeviation", "2"), new Attribute("flood-opacity", "0.4")])
                ]),
                new Node("radialGradient",  [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("id", "highlight"), new Attribute("cx", "50%"), new Attribute("cy", "50%"), new Attribute("r", "50%"), new Attribute("fx", "50%"), new Attribute("fy", "25%")], undefined, [
                    new Node("stop", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("offset", "0%"), new Attribute("stop-color", "#FF4D4D")]),
                    new Node("stop", [new Attribute("ns", "http://www.w3.org/2000/svg"), new Attribute("offset", "100%"), new Attribute("stop-color", "#8B0000")])
                ])
            ].concat(drawHole(canvases[canvasNumber - 1]))),
            new Node("h3", [new Attribute("class", "canvasTarget"), new Attribute("id", "canvas" + canvasNumber + "Target")], "Target " + canvases[canvasNumber - 1].target),
            new Node("div", [new Attribute("class", "canvasScores"), new Attribute("id", "canvas" + canvasNumber + "Scores")])
        ]));
        return canvas;
    }
    
    function drawHole(canvas) {
        const circlePositions = canvas.circles;
        let circleNum = 1;
        let circles = [];
        circlePositions.forEach(circle => {
            circles.push(new Node("circle", [
                new Attribute("ns", "http://www.w3.org/2000/svg"),
                new Attribute("id", "circle" + circleNum),
                new Attribute("cx", circle.x),
                new Attribute("cy", circle.y),
                new Attribute("r", 1000/12),
                new Attribute("fill", "url(#highlight)"),
                new Attribute("filter", "url(#shadow)")]))
            circleNum++;
        });
    
        return circles;
    }
    
    function buildNode(node = new Node("div")) {
        let nodeComp = document.createElement(node.type);
        if (node.attributes != null && typeof node.attributes !== "undefined" && node.attributes.length > 0) {
            let ns;
            for (let position = 0; position < node.attributes.length; position++) {
                if (node.attributes[position].type == "ns") {
                    ns = node.attributes[position].value;
                    nodeComp = document.createElementNS(ns, node.type);
                    continue;
                }
                if (ns != null && typeof ns !== "undefined") {
                    try {
                        nodeComp.setAttribute( node.attributes[position].type, node.attributes[position].value);
                        continue;
                    }
                    catch (err) {
    
                    }
                }
                let attribute = document.createAttribute(node.attributes[position].type);
                attribute.value = node.attributes[position].value;
                nodeComp.setAttributeNode(attribute);
            }
        }
        if (node.content != null && typeof node.content !== "undefined") {
            nodeComp.innerHTML = node.content;
        }
        if (node.childNodes != null && typeof node.childNodes !== "undefined" && node.childNodes.length > 0) {
            for (let childNode = 0; childNode < node.childNodes.length; childNode++) {
                nodeComp.insertAdjacentElement("beforeend", buildNode(node.childNodes[childNode]));
            }
        }
        return nodeComp;
    }
    
    document.getElementById("canvas-container").insertAdjacentElement("beforeend", buildCanvas(1));
    <div id="canvas-container"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search