skip to Main Content

I am creating circles for fun. I am generating them from a function. I want to be able to change their parameters in the console once they are created. Why can’t I do this? My question is specific to this method – why am I not able to change them this way?

a.fill = "black"; written in the console does not change the parameter of the circle object

const container = document.body
let svgContainer = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svgContainer.style.width = "100vw"
svgContainer.style.height = "100vw"
svgContainer.setAttribute("viewBox", "0 0 500 750");
container.appendChild(svgContainer);

function makeCircle(cx, cy, r, fill, stroke, id) {
  this.cx = cx;
  this.cy = cy;
  this.r = r;
  this.fill = fill;
  this.stroke = stroke;
  this.id = id;
  let newCircle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
  newCircle.setAttribute("cx", cx);
  newCircle.setAttribute("cy", cy);
  newCircle.setAttribute("r", r);
  newCircle.setAttributeNS(null, 'fill', fill);
  newCircle.setAttributeNS(null, 'stroke', stroke);
  svgContainer.appendChild(newCircle);
}

let a = new makeCircle(50, 300, 20, '#493fea', 'green');

2

Answers


  1. Since you are doing those circles for fun. Here is a modern funny way:

    Run the SO snippet; click the viewBox

    customElements.define("svg-measles", class extends HTMLElement {
      connectedCallback() {
        this.style.display = "inline-block";
        this.style.width   = "100vw";
        this.style.height  = "100vh";
        this.colors = ['red','orange','yellow','green','blue','indigo','violet'];
        this.innerHTML = `<svg style="width:100%;height:100%" viewBox="${this.getAttribute("viewbox")}"/>`;
        let svg = this.querySelector("svg");
        this.makeCircle("50%", "50%", 10, '#493fea', 'green');
        this.onclick = (evt) => {
          let svg = this.querySelector("svg");
          let pt  = svg.createSVGPoint();
          pt.x = evt.clientX;
          pt.y = evt.clientY;
          let {x,y} = pt.matrixTransform(svg.getScreenCTM().inverse());
          this.makeCircle(x, y, 10, 'green', 'yellow');
        }
      }
      makeCircle(cx, cy, r, fill, stroke, id) {
        this.colors.unshift(fill = this.colors.pop());
        this.querySelector("svg")
            .insertAdjacentHTML("beforeend", 
                                `<circle cx="${cx}" cy="${cy}" r="${r}" fill="${fill}" stroke="${stroke}"/>`);
      }
    });
    <svg-measles viewBox="0 0 300 200"></svg-meales>
    Login or Signup to reply.
  2. I want to be able to change their parameters in the console once they are created. Why can’t I do this?

    because you create a new object with a property cx for example and seperately from that, a <circle cx="..." /> and the property in your created object and the attribute on the <circle /> have no connection to each other after they were once initially set. You’d have to make such a connection so that when you change the property the attribute is updated.

    Here an example that does that. It’s a somewhat generic function that takes the name of an element and a list of its specific SVG attributes and then returns a function like your makeCircle. But with the mentioned properties on the object.

    Since it’s generic it doesn’t take attributes like makeCircle(cx, cy, r, fill, stroke, id) but instead a single object with the attributes you want to set: makeCircle({ cx, cy, r, fill, stroke, id }).

    // a few tools:
    
    // to turn `stroke-width` into `strokeWidth`
    const camelCase = str => str.toLowerCase().replace(/-[a-z]/gi, v => v[1].toUpperCase());
    
    // takes a list of attribute names and creates a bunch of property definitions that get/set that value from the attribute
    // see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties
    const attrs2props = (attrs) => Object.fromEntries(attrs.map(attr => [camelCase(attr), {
      configurable: true,
      get() {
        return this.getAttributeNS(null, attr)
      },
      set(value) {
        this.setAttributeNS(null, attr, value)
      }
    }]));
    
    const sharedSVGAttributes = attrs2props(["clip-path", "clip-rule", "color", "color-interpolation", "cursor", "display", "fill", "fill-opacity", "fill-rule", "filter", "mask", "opacity", "pointer-events", "shape-rendering", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "transform", "vector-effect", "visibility"]);
    
    const factory = (name, svgAttributes) => {
      const descriptors = { ...sharedSVGAttributes, ...attrs2props(svgAttributes) };
    
      return init => Object.assign(
        Object.defineProperties(
          document.createElementNS("http://www.w3.org/2000/svg", name),
          descriptors
        ),
        init
      )
    }
    
    
    
    const container = document.body
    let svgContainer = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svgContainer.style.width = "100vw"
    svgContainer.style.height = "100vw"
    svgContainer.setAttribute("viewBox", "0 0 500 750");
    container.appendChild(svgContainer);
    
    // create two functions that return the respective svg element ...
    const makeCircle = factory("circle", ["cx", "cy", "r"]);
    const makeRect = factory("rect", ["x", "y", "width", "height", "rx", "ry"]);
    
    let a = makeCircle({
      cx: 50,
      cy: 300,
      r: 20,
      fill: '#493fea',
      stroke: 'green'
    });
    
    let b = makeRect({
      x: 10,
      y: 10,
      width: 100,
      height: 100,
      fill: "blue"
    })
    
    svgContainer.appendChild(a);
    svgContainer.appendChild(b);
    
    // ... but they also have properties to change the values.
    a.r = 100;
    a.fill = "red";
    
    b.width = 20;
    b.stroke = "magenta";
    b.strokeWidth = 5;

    This is a very basic example, there’s probably a lot to improve upon this.

    The code currently doesn’t take into account that some properties should return numbers, every property is a string!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search