How to create a valid svg element with javascript - javascript

I was working with SVG and i created an svg element. I added a <rect> element directly into the svg with html and then I created a new element (without namespace) <circle> with javascript and appended it to the svg element. The <rect> element displayed in the svg viewbox but the <circle> element did not display.
I got the <rect> and <circle> on the console and checked the constructor. The <rect> element returned SVGRectElement but the <circle> returned HTMLUnknownElement. I created a new <circle> element (with namespace: https://www.w3.org/2000/svg) and checked the constructor which returned Element.
Whichever way, appending both the namespaced and non-namespaced <circle> element to the svg did not appear in the svg viewbox. So how do i create a recognized svg element with javascript that will return SVGCircleElement?.
var circle = document.createElement('circle');
circle.setAttribute('cx', '10');
circle.setAttribute('cy', '10');
circle.setAttribute('r', '30');
circle.setAttribute('fill', 'red');
var circle_2 = document.createElementNS('https://www.w3.org/2000/svg','circle');
circle.setAttribute('cx', '5');
circle.setAttribute('cy', '20');
circle.setAttribute('r', '30');
circle.setAttribute('fill', 'blue');
var svg = document.getElementById('svgx');
var rect = document.getElementById('svgrect');
svg.appendChild(circle);
svg.appendChild(circle_2);
console.log(svg.constructor); // SVGSVGElement() { [native code] }
console.log(rect.constructor); // HTMLRectElement() { [native code] }
console.log(circle.constructor); // HTMLUnknownElement() { [native code] }
console.log(circle_2.constructor); // Element() { [native code] }
<svg style='width: 100%;' id='svgx'>
<rect x='5' y='5' width='50' height='30' fill='black' id='svgrect'>
</svg>

Remove the "s" from "https".
var circle_2 = document.createElementNS('http://www.w3.org/2000/svg','circle');
Even though it looks like a URL. It is really just a string constant that indicates this XML file is an SVG file. The namespace constant has to be exactly as presented here.
Also you need to change circle to circle_2 for that section of code.
var circle_2 = document.createElementNS('http://www.w3.org/2000/svg','circle');
circle_2.setAttribute('cx', '5');
circle_2.setAttribute('cy', '20');
circle_2.setAttribute('r', '30');
circle_2.setAttribute('fill', 'blue');
var svg = document.getElementById('svgx');
var rect = document.getElementById('svgrect');
svg.appendChild(circle_2);
<svg style='width: 100%;' id='svgx'>
<rect x='5' y='5' width='50' height='30' fill='black' id='svgrect'>
</svg>

Related

How can I make SVG text editable?

I have a text tag that I'm adding to SVG via JS script and would like to make that text editable.
Found a couple of solutions on StackOverflow:
creating a css class and applying it to my SVG text element. This works perfectly, at least in Safari, but isn't recommended it by MDN https://developer.mozilla.org/en-US/docs/Web/CSS/user-modify
Here is the class that is actually someone's answer, unfortunately I can't remember whose:
.editable {
font-size: 0.3em;
user-modify: read-write;
-moz-user-modify: read-write;
-webkit-user-modify: read-write;
}
wrapping the svg in a contenteditable div (answer given by Erik). This also works but results in bad cursor behavior, at least in Safari.
<div contenteditable="true">
<svg id="svgArea" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
</svg>
</div>
The JS part
function foo(x, y, w, h) {
var rect, group;
var svgPlace = document.getElementById('svgArea');
var xmlns = "http://www.w3.org/2000/svg";
group = document.createElementNS(xmlns, 'g');
svgPlace.appendChild(group);
var txtElem = document.createElementNS(xmlns, 'text');
txtElem.setAttributeNS(null, 'x', x);
txtElem.setAttributeNS(null, 'y', y * 1.25);
txtElem.setAttributeNS(null, 'fill', 'LightBlue');
txtElem.setAttributeNS(null, 'contentEditable', true); //does nothing!
txtElem.setAttributeNS(null, 'class', 'editable'); //this is the best working option at the moment
var txtVal = document.createTextNode('test');
txtElem.appendChild(txtVal);
group.appendChild(txtElem);
}
I'd love to be able to set the contentEditable as an attribute, if that's possible. But most importantly, what's the best way to make SVG text editable?
#Raymond comment is the correct answer but since you still have problem I wrote down an example.
document.getElementById("svgWrapper").contentEditable = "true";
<div id="svgWrapper">
<svg id="svgArea" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
</svg>
</div>
I ended up solving this by placing an input tag inside a foreign object, which is added to an SVG group. That works on Safari so I wanted to post it here, in case anyone else is struggling with the same thing.
<svg>
<g>
<foreignobject>
<input></input>
</foreignobject>
</g>
</svg>
If you need to add using JS:
function addSvg(x, y, w, h) {
var rect, group;
var svgPlace = document.getElementById('svgArea');
var xmlns = "http://www.w3.org/2000/svg";
group = document.createElementNS(xmlns, 'g');
svgPlace.appendChild(group);
// solution
let foreigner = document.createElementNS(xmlns, "foreignObject");
foreigner.setAttributeNS(null, "x", x);
foreigner.setAttributeNS(null, "y", y);
foreigner.setAttributeNS(null, "width", w);
foreigner.setAttributeNS(null, "height", h);
group.appendChild(foreigner);
let txt = document.createElement('input');
foreigner.appendChild(txt);
}

Dynamically adding elements to loaded svg file

After loading an svg file from InkScape I want to add new elements to the drawing, created under program control. This seems to work fine if we just change the attributes of already present elements, but new created ones, even if they appear when inspecting the DOM, do not show!
A hand-simplified SVG test file:
<svg id="svg8" width="1e3" height="750" version="1.1"
viewBox="0 0 264.58333 198.43751" xmlns="http://www.w3.org/2000/svg">
<g id="layer"><circle id="cc0" cx="20" cy="20" r="10"/></g>
</svg>
The javascript/html file:
<!doctype html><html><head><meta charset="UTF-8"/>
<script>
function addCircle() {
var svgDoc = document.getElementById("test");
var svg = svgDoc.contentDocument;
var svgns = svgDoc.namespaceURI;
// Ok, this changes the circle to red
var c0 = svg.getElementById("cc0");
c0.setAttribute("fill", "#ff0000");
var layer = svg.getElementById("layer");
// Create a circle inside layer "g"
var cc = document.createElementNS(svgns, "circle");
cc.setAttribute("cx", "50");
cc.setAttribute("cy", "50");
cc.setAttribute("r", "20");
layer.appendChild(cc);
// However it's not updating the screen
// even if DOM shows the circle is there, inside the "g"
}
</script></head>
<body>
<object id="test" data="test.svg" type="image/svg+xml"></object>
<script>
document.onreadystatechange = function(){
if(document.readyState === 'complete') addCircle(); }
</script>
</body>
</html>
What am I doing wrong? Or what am I missing? Thank you!
Its the issue with your svg namespace that you are passing to create the circle. Try this
var cc = document.createElementNS("http://www.w3.org/2000/svg", "circle");
You are getting the namespaceURI of the <object> element.
What you need is the one of the inner document's documentElement :
function addCircle() {
// this is the <object>
var svgDoc = document.getElementById("test");
var svg = svgDoc.contentDocument;
// here get the real svg document
var svgns = svg.documentElement.namespaceURI;
//...
As a plunker since stacksnippetsĀ® won't allow the modification of inner frames...

Dynamically add a full svg shape (with pure Javascript)

I want to draw a square by using path in SVG created with JS. But the browsers do not accept this:
Javascript:
var svg = document.createElement('svg');
svg.width = "200";
svg.height = "200";
document.body.appendChild(svg);
var path = document.createElement('path');
path.setAttribute('d','M100,0 L200,100 100,200 0,100Z');
path.setAttribute('fill','red');
svg.appendChild(path);
HTML (output):
<svg width="200" height="200">
<path d="M100,0 L200,100 100,200 0,100Z" fill="red"/>
</svg>
createElement can only be used to create html elements. To create SVG elements you must use createElementNS and supply the SVG namespace as the first argument.
Also document.body.appendChild('svg'); is presumably a typo as you want to add the svg element and not a string containing the text 'svg'
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width','200');
svg.setAttribute('height','200');
document.body.appendChild(svg);
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d','M100,0 L200,100 100,200 0,100Z');
path.setAttribute('fill','red');
svg.appendChild(path);
Use createElementNS instead of createElement.

SVG element inserted into DOM is ignored (its type is changed)

I am using the VivaGraph.js library to render a graph in SVG. I am trying to display an image cropped to a circle, for which I am using a clipPath element - as recommended in this post.
However, when I create a new SVG element of type that has a capital letter in it, e.g. clipPath in my case, the element that is inserted into the DOM is lowercase, i.e. clippath, even though the string I pass in to the constructor is camelCase. Since SVG is case sensitive, this element is ignored. Everything else seems to be okay.
I also tried to change the order in which I append the child elements, in hopes of changing the 'z-index', but it didn't have an impact on this.
I am using the following code inside of the function that creates the visual representation of the node in the graph (the 'addNode' callback) to create the node:
var clipPhotoId = 'clipPhoto';
var clipPath = Viva.Graph.svg('clipPath').attr('id', clipPhotoId);
var ui = Viva.Graph.svg('g');
var photo = Viva.Graph.svg('image').attr('width', 20).attr('height', 20).link(url).attr('clip-path', 'url(#' + clipPhotoId + ')');
var photoShape = Viva.Graph.svg('circle').attr('r', 10).attr('cx', 10).attr('cy', 10);
clipPath.append(photoShape);
ui.append(clipPath);
ui.append(photo);
return ui;
Thank you!
There is a bit of tweaking needed on top of the post you provided.
General idea to solve your issue is this one:
We create a VivaGraph svg graphics (which will create an svg element in the dom)
Into this svg graphic we create only once a clip path with relative coordinates
When we create a node we refer to the clip path
Code is:
var graph = Viva.Graph.graph();
graph.addNode('a', { img : 'a.jpg' });
graph.addNode('b', { img : 'b.jpg' });
graph.addLink('a', 'b');
var graphics = Viva.Graph.View.svgGraphics();
// Create the clipPath node
var clipPath = Viva.Graph.svg('clipPath').attr('id', 'clipCircle').attr('clipPathUnits', 'objectBoundingBox');
var circle = Viva.Graph.svg('circle').attr('r', .5).attr('cx', .5).attr('cy', .5);
clipPath.appendChild(circle);
// Add the clipPath to the svg root
graphics.getSvgRoot().appendChild(clipPath);
graphics.node(function(node) {
return Viva.Graph.svg('image')
.attr('width', 30)
.attr('height', 30)
// I refer to the same clip path for each node
.attr('clip-path', 'url(#clipCircle)')
.link(node.data.img);
})
.placeNode(function(nodeUI, pos){
nodeUI.attr('x', pos.x - 15).attr('y', pos.y - 15);
});
var renderer = Viva.Graph.View.renderer(graph, { graphics : graphics });
renderer.run();
The result in the dom will be like this:
<svg>
<g buffered-rendering="dynamic" transform="matrix(1, 0, 0,1,720,230.5)">
<line stroke="#999" x1="-77.49251279562495" y1="-44.795726056131116" x2="6.447213894549255" y2="-56.29464520347651"></line>
<image width="30" height="30" clip-path="url(#clipCircle)" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="a.jpg" x="-92.49251279562495" y="-59.795726056131116"></image>
<image width="30" height="30" clip-path="url(#clipCircle)" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="b.jpg" x="-8.552786105450746" y="-71.2946452034765"></image>
</g>
<clipPath id="clipCircle" clipPathUnits="objectBoundingBox">
<circle r="0.5" cx="0.5" cy="0.5"></circle>
</clipPath>
</svg>
Notice the clipPathUnits="objectBoundingBox", since it's the main trick for this solution.

Dynamically rendered SVG is not displaying

I have the following svg object which if I put in html page directly without using code(static) it renders properly.
but same svg content if I insert into my html page using JavaScript it is not showing and if I open it in firebug and inspect svg and try to edit svg tag, it displays.
What could be the problem
<svg height="100" width="100">
<rect width="100" height="100" style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)"></rect>
</svg>
I am adding svg dynamically using below code, here container will be my div which is there under body
viewPort = document.createElementNS('http://www.w3.org/2000/svg','svg');
viewPort.setAttribute('height', 100);
viewPort.setAttribute('width', 100);
container.innerHTML = '';
container.appendChild(viewPort);
After this I am adding rect inside this using
boardElement = document.createElement('rect');
boardElement.setAttribute('width', '100');
boardElement.setAttribute('height', '100');
boardElement.setAttribute('y', '1');
boardElement.setAttribute('x', '1');
boardElement.setAttribute('style', "fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)");
viewPort.appendChild(boardElement);
The element boardElement should be declared like so
boardElement = document.createElementNS("http://www.w3.org/2000/svg", "rect");

Categories

Resources