Dynamically add a full svg shape (with pure Javascript) - 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.

Related

How to create a valid svg element with 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>

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...

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.

How to use the SVG checkintersection() function correctly?

I'm having a problem with the SVG checkintersection() function. All I want to do is to check whether a small SVG-rectangle intersects the area of an SVG-path, but I can't figure out what to call the function on (I already tried to call it on the SVG DOM object, among several other things google turned up).
So what I need to know is what to put in for the placeholder ("foo") in this snippet:
var closedPath = document.getElementById(closedPath);
var rectangle = document.getElementById(rectangle);
if (foo.checkIntersection(closedPath, rectangle)) {
//do stuff
};
with the HTML being something along the lines of
<html>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svgroot">
<g>
<path id="closedPath" fill="{$c5}" d="M-250179-46928l-5051 1351l-867-1760l-33-146l-12-99l-82-678l-17-249l86-644l305-1800l158-2882l75-1425l-47-280l-22-131l-137-411l-300-892l1273 620l931-109l1957-734l1860-1096l292-192l884 547l2690 2153l480 963l36 244l-948 1878l-376 591l-60 567l-72 1147l97 847l-222 334l-122 117l-2403 2093l-353 76z"/>
<rect id="rectangle" fill="white" x="-126828" y="0" width="45000" height="45000"/>
</g>
</svg>
</body>
</html>
Any help would be much appreciated!
Edit: Just wanted to add that I now use a workaround, which consists of converting the SVG path to an array of point coordinates using a parser function I wrote, which is then put into a simple coordinate-test function.
Also this may have been a solution Hit-testing SVG shapes?
checkIntersection is a method on the <svg> element so you'd want something like this...
var svg = document.getElementById("svgroot");
var closedPath = document.getElementById(closedPath);
var rectangle = document.getElementById(rectangle);
var rect = svg.createSVGRect();
rect.x = rectangle.animVal.x;
rect.y = rectangle.animVal.y;
rect.height = rectangle.animVal.height;
rect.width = rectangle.animVal.width;
svg.checkIntersection(closedPath, rect) {
// do stuff
}
Note also how the second argument has to be an SVGRect and not an element.
SVG elements support SMIL animation, you could equally well write rectangle.baseVal.x etc but that wouldn't necessarily reflect the rectangle's current position if you were animating the rectangle. If you're not using SMIL then rectangle.baseVal.x = rectangle.animVal.x
Because a <rect> can have things like rounded corners it doesn't have an SVGRect interface so you have to convert from the interface it does have (SVGRectElement) to the one you need (SVGRect)
<svg width="390" height="248" viewBox="-266600, -68800, 195000, 124000" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path id="closedPath" fill="#ff9966" d="M-250179-46928l-5051 1351l-867-1760l-33-146l-12-99l-82-678l-17-249l86-644l305-1800l158-2882l75-1425l-47-280l-22-131l-137-411l-300-892l1273 620l931-109l1957-734l1860-1096l292-192l884 547l2690 2153l480 963l36 244l-948 1878l-376 591l-60 567l-72 1147l97 847l-222 334l-122 117l-2403 2093l-353 76z"/>
<rect id="rectangle" fill="#66ff66" x="-126828" y="0" width="45000" height="45000"/>
</svg>
<script>
var rectangle = document.getElementById('rectangle');
var closedPath = document.getElementById('closedPath');
var svgRoot = closedPath.farthestViewportElement;
var rect = svgRoot.createSVGRect();
rect.x = rectangle.x.animVal.value;
rect.y = rectangle.y.animVal.value;
rect.height = rectangle.height.animVal.value;
rect.width = rectangle.width.animVal.value;
var hasIntersection = svgRoot.checkIntersection(closedPath, rect);
console.log(hasIntersection);
</script>

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