How do I create an SVG anchor through JavaScript? Please see relevant section and an example from spec. How do I convert this example to JavaScript (basically, how to dynamically generate the container element a so that when I click the ellipse, it navigates away.
<?xml version="1.0"?>
<svg width="5cm" height="3cm" viewBox="0 0 5 3" version="1.2" baseProfile="tiny"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Example 17_01</title>
<desc>A simple link on an ellipse.</desc>
<rect x=".01" y=".01" width="4.98" height="2.98"
fill="none" stroke="blue" stroke-width=".03"/>
<a xlink:href="http://www.w3.org/">
<ellipse cx="2.5" cy="1.5" rx="2" ry="1"
fill="red" />
</a>
</svg>
This is just basic DOM:
var xlinkNS="http://www.w3.org/1999/xlink", svgNS="http://www.w3.org/2000/svg";
var a = document.createElementNS(svgNS, "a");
a.setAttributeNS(xlinkNS,"href","http://www.w3.org/");
var ellipse = document.createElementNS(svgNS, "ellipse");
ellipse.setAttributeNS(null,"cx","2.5");
ellipse.setAttributeNS(null,"cy","1.5");
ellipse.setAttributeNS(null,"rx","2");
ellipse.setAttributeNS(null,"ry","1");
ellipse.setAttributeNS(null,"fill","red");
a.appendChild(ellipse);
document.documentElement.appendChild(a);
Using my function below, it's as easy as this:
// Find the first SVG element
var svg = document.getElementsByTagName('svg')[0];
var a = createOn(svg,'a',{'xlink:href':'http://www.w3.org/'});
createOn(a,'ellipse',{cx:2.5,cy:1.5,rx:1,ry:1,fill:'red'});
function createOn(root,name,attrs,text){
var doc = root.ownerDocument,
svg = root.ownerSVGElement || root; // In case the root _is_ the <svg>
var svgNS = svg.getAttribute('xmlns');
var el = doc.createElementNS(svgNS,name);
for (var attr in attrs){
if (!attrs.hasOwnProperty(attr)) continue;
var parts = attr.split(':');
if (parts[1]) el.setAttributeNS(
svg.getAttribute('xmlns:'+parts[0]),parts[1],attrs[attr]
);
else el.setAttributeNS(null,attr,attrs[attr]);
}
if (text) el.appendChild(document.createTextNode(text));
return root.appendChild(el);
}
If you already have the ellipse and want to wrap it, then create the 'a' element and:
// Get a reference to the ellipse however you like
var ellipse = document.getElementsByTagName('ellipse')[0];
// Put the anchor node immediately preceding the ellipse
ellipse.parentNode.insertBefore(a,ellipse);
// Move the ellipse to be a child of the anchor
a.appendChild(ellipse);
Related
Context
I'm using react js and svg.draw.js for draw svg elements inside a div, the user can draw rect or other shapes and he can see shapes drawed in precedent session.
For make the last functionality I stored the entire svg element drawed as string so when ComponentDidMount() method is fired, it can parse it to image/svg+xml element and then it can append the svg node to the svg root.
So to try this solution in ComponentDidMount() I have create a rect node to append.
Issue
When I try to append the rect element, it doesn't visualized but it's present inside the root element.
This is what I see in console, the rect element is what I have to append.
Code
This is what it's present in ComponentDidMount() method.
componentDidMount(){
/*
*
*irrilevant code*/
const scriptDraw = document.createElement("script");
scriptDraw.src = "svgDrawer.js"; //svg.draw.js
scriptDraw.setAttribute("id","drawer")
scriptDraw.async = true;
document.body.appendChild(scriptDraw);
var str = '<rect id="SvgjsRect1008" class="selected" width="124" height="89" stroke="#3399ff" stroke-width="2" fill-opacity="1" fill="#3399ff" x="236" y="160" name="dawdawad"></rect>'
var parser = new DOMParser();
var doc = parser.parseFromString(str, "image/svg+xml");
scriptDraw.addEventListener("load",function(){
var map = document.getElementById("planimetriaSVG"); //svg root
map.appendChild(doc.documentElement);
})
}
Add a namespace attribute to the rect element so parseFromString knows its supposed to be an SVG element i.e.
var str = '<rect xmlns="http://www.w3.org/2000/svg" id="SvgjsRect1008" class="selected" width="124" height="89" stroke="#3399ff" stroke-width="2" fill-opacity="1" fill="#3399ff" x="236" y="160" name="dawdawad"></rect>'
var parser = new DOMParser();
var doc = parser.parseFromString(str, "image/svg+xml");
The following circle tag is inside tag:
<circle cx="111.70110487400142" cy="134.60212936975006" r="10" stroke="#000000" fill="#000000" stroke-width="0" data-pdf-annotate-id="8e1b063b-ec08-4c73-8b86-d8e8cce5615e" data-pdf-annotate-type="fillcircle" aria-hidden="true" transform="scale(1.33) rotate(0) translate(0, 0)"></circle>
Now i have to append this generated circle inside a tag like
<a href="#">
<circle cx="111.70110487400142" cy="134.60212936975006" r="10" stroke="#000000" fill="#000000" stroke-width="0" data-pdf-annotate-id="8e1b063b-ec08-4c73-8b86-d8e8cce5615e" data-pdf-annotate-type="fillcircle" aria-hidden="true" transform="scale(1.33) rotate(0) translate(0, 0)"></circle>
</a>
Updated:
<svg class="annotationLayer" width="1118.53" height="1582.7" data-pdf-annotate-container="true" data-pdf-annotate-viewport="{"viewBox":[0,0,841,1190],"scale":1.33,"rotation":0,"offsetX":0,"offsetY":0,"transform":[1.33,0,0,-1.33,0,1582.7],"width":1118.53,"height":1582.7,"fontScale":1.33}" data-pdf-annotate-document="/uploads/docs/Vishnu/file/IBC-CW1a-DE-02_2.pdf" data-pdf-annotate-page="1" style="width: 1118.53px; height: 1582.7px;">
<circle cx="138.76877404693374" cy="82.72243012162977" r="10" stroke="#000000" fill="#000000" stroke-width="0" data-pdf-annotate-id="b91a7011-656c-48d6-9f1c-62ac4bfc4f91" data-pdf-annotate-type="fillcircle" aria-hidden="true" transform="scale(1.33) rotate(0) translate(0, 0)"></circle>
</svg>
function createCircle(a) {
var circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
(0, _setAttributes2.default)(circle, {
cx: a.cx,
cy: a.cy,
r: a.r
});
var spot_anchor = document.createElement("a")
console.log(spot_anchor)
spot_anchor.appendChild(circle)
console.log(spot_anchor)
console.log('Create_circl1')
return circle;
}
How can i able to do by using javascript ?
You are creating your <a> element the incorrect way. You are using:
document.createElement("a")
This creates an <a> element in the HTML namespace. In other words, an HTML <a> element.
You need to create an SVG <a> element, which is completely different, even though it has the same name.
You do that in the same way that you created the <circle> element:
document.createElementNS('http://www.w3.org/2000/svg', 'a');
Your circle needs to be inside an svg tag, otherwise it is meaningless in your html. So create a wrapping SVG in the same way you make the circle, then append the circle to that, and the svg to your anchor:
function createCircle( a ){
var svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
var circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
var anchor = document.createElement( 'a' );
circle.setAttribute( 'cx', a.cx );
circle.setAttribute( 'cy', a.cy );
circle.setAttribute( 'r', a.r );
svg.setAttribute( 'viewBox', `${a.x - a.r} ${a.y - a.r} ${a.r * 2} ${a.r * 2}` );
svg.setAttribute( 'width', `${a.r * 2}` );
svg.setAttribute( 'height', `${a.r * 2}` );
svg.appendChild( circle );
anchor.appendChild( svg );
return anchor;
}
document.body.appendChild( createCircle({ cx: 10, cy: 10, r: 10 }) );
You should not add attributes such as fill and stroke to your a tag directly, as those attributes are not supported and invalid. You should use data attributes in that case. Maybe even consider just using data-svg-attributes="{'cx':10,'cy':10,'r':10}" and use a JSON.parse when you need to to get the right data out. Update: The fill and stroke attributes will be inherited if you declare them in the wrapping tag's style attribute, so you could use that (aka style="stroke: red;").
appendChild, replaceNode etc will remove the node from the tree before relocating it, so (since you asked it vaguely I have 0 idea if the atag exists or not, so I assume it is there, otherwise create it using createElementNS):
yourSvg = document.querySelector("svg");
yourCircle = svg.querySelector("circle");
yourATag = svg.querySelector("a");
yourATag.appendChild(yourCircle)
The a tag is not a visual element, its bounding box will be equal to what is inside. In case you are wondering:
https://jsfiddle.net/ibowankenobi/4t44n8jo/4/
I need to add SVG objects to specific locations inside an SVG object that's appended to the DOM.
But whenever I do that I see nothing rendered on the screen. I can see the SVG objects are added (in Elements tab of DevTools) but they're not rendered. They're pure SVG (not wrapped around an HTML element like a DIV).
I've tried loading SVGs with ajax and adding them, tried to do with Snap, tried to have these elements inside a <defs> tag, find them with Snap and then add them to the main Snap object. Nothing seems to work. The objects are always added but not rendered.
Is that even possible?
The SVG
<svg width="400" height="300" style="background: gray">
<defs>
<circle id="redc" cx="50" cy="50" r="50" style="fill: red" />
<circle id="yelc" cx="40" cy="40" r="40" style="fill: yellow" />
</defs>
<circle id="bluc" cx="200" cy="200" r="50" style="fill: blue" />
</svg>
JavaScript
const s = Snap("#root");
Snap.load('images/all.svg', function(data){
var all = data;
// append the all.svg node. cool
s.append( all.node );
// get the red circle definition
var redc = all.select('#redc');
s.append(redc.node); // doesn't work
});
with foreign object:
Snap.load('images/all.svg', function(data){
var all = data;
// append the all.svg node. cool
s.append( all.node );
// get the red circle definition
var redc = all.select('#redc');
// foreign object
var foreign = document.createElementNS('http://www.w3.org/2000/svg',"foreignObject");
foreign.setAttribute('width', 500);
foreign.setAttribute('height', 150);
foreign.appendChild(redc);
// add the foreign object - doesn't work
s.append( foreign );
});
It doesn't work because you're appending the circle outside of the <svg> tree i.e. directly under #root which is probably some kind of HTML element such as a <div>
The foreignObject problem is basically the same. Not sure why you're trying to add a circle as a child of a foreignObject (that won't work as you'd need an svg element to be its parent). I've used an html element instead.
const s = Snap("#root");
var svg = '<svg width="400" height="300" style="background: gray"><defs><circle id="redc" cx="50" cy="50" r="50" style="fill: red" /><circle id="yelc" cx="40" cy="40" r="40" style="fill: yellow" /></defs><circle id="bluc" cx="200" cy="200" r="50" style="fill: blue" /></svg>';
var all = Snap.parse(svg);
// append the all.svg node. cool
s.append( all.node );
// get the red circle definition
var redc = all.select('#redc');
all.node.append(redc.node); // append as a child of the svg node
// foreign object
var foreign = document.createElementNS('http://www.w3.org/2000/svg',"foreignObject");
foreign.setAttribute('width', 500);
foreign.setAttribute('height', 150);
foreign.setAttribute('fill', 'pink');
var p = document.createElement('p');
foreign.appendChild(p);
var text = document.createTextNode("Hello World");
p.appendChild(text);
// add the foreign object to the correct part of the tree
all.node.append( foreign );
<script src="http://snapsvg.io/assets/js/snap.svg-min.js"></script>
<div id="root"></div>
I am creating a program which converts a SVG file to my own format. I have created a web based application to do this. I use the default DOM parsing functionality of web browsers to iterate over the SVG contents.
With Javascript I can get a SVG path element using:
var path = document.getElementById("path3388");
I can get the path segments using:
var pathSegments = path.pathSegList
However these path segments are relative to whatever parent SVG element is defined. Transforms are not included in the path segment list.
Is there a way to get the absolute coordinates of this path as they are ultimately used when drawn on the screen?
Example: say I got the following SVG snippet:
<g transform="translate(100, 100)">
<g transform="translate(50, 50)">
<path d="M 0,0 10,0 10,10"></path>
</g>
</g>
What I want is to retrieve is the coordinates of the path with the transforms of the two g elements applied. In this case the coordinates of the path should be:
[150,150], [160, 150], [160, 160]
You want is to do something like this to each path segment coordinate...
var root = document.getElementById("root");
var path = document.getElementById("path");
var point = root.createSVGPoint();
point.x = 0; // replace this with the x co-ordinate of the path segment
point.y = 0; // replace this with the y co-ordinate of the path segment
var matrix = path.getTransformToElement(root);
var position = point.matrixTransform(matrix);
alert(position.x + ", " + position.y);
<svg id="root">
<g transform="translate(100, 100)">
<g transform="translate(50, 50)">
<path id="path" d="M 0,0 10,0 10,10"></path>
</g>
</g>
</svg>
If you find that there's no getTransformToElement function any more since it's been removed in SVG 2 then this polyfill will restore that missing method.
path.getTransformToElement() is no longer supported in Chrome as of v48.
A slightly simpler method might entail...
const path = document.getElementById("path");
const pathBBox = path.getBBox();
console.log(pathBBox.x, pathBBox.y);
I'm trying to append a path element to some inline svg using javascript but I'm getting the error: "Uncaught NotFoundError: An attempt was made to reference a Node in a context where it does not exist"
Can anyone explain why this javascript is not working with my HTML?
<body>
<svg height='500' width='500'></svg>
<script>
var svg = document.getElementsByTagName("svg")[0];
var pathElement = svg.appendChild("path");
</script>
</body>
appendChild takes an element and not a string so what you need is something like this...
var svg = document.getElementsByTagName("svg")[0];
var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
// Use path.setAttribute("<attr>", "<value>"); to set any attributes you want
var pathElement = svg.appendChild(path);
Working example for anyone passing by who wants to see it in action:
http://jsfiddle.net/huvd0fju/
HTML
<svg id="mysvg" width="100" height="100">
<circle class="c" cx="50" cy="50" r="40" fill="#fc0" />
</svg>
JS
var mysvg = document.getElementById("mysvg");
//uncomment for example of changing existing svg element attributes
/*
var mycircle = document.getElementsByClassName("c")[0];
mycircle.setAttributeNS(null,"fill","#96f");
*/
var svgns = "http://www.w3.org/2000/svg";
var shape = document.createElementNS(svgns, "rect");
shape.setAttributeNS(null,"width",50);
shape.setAttributeNS(null,"height",80);
shape.setAttributeNS(null,"fill","#f00");
mysvg.appendChild(shape);