Javascript-Generated SVG Not Updating - javascript

I have a script that adds elements to an inline SVG image via jQuery, but the new elements don't seem to be showing up. The output is perfectly valid; I can copy it into the original file, reload it, and it will render just fine. But when the script generates it, the new elements aren't visible.
Here is a snippet that replicates the problem: http://tinkerbin.com/7OmDWlsz
Thanks in advance!

You will not see any svg output if the elements are not in the svg namespace.
Try replacing your script snippet with this:
var slices = 10;
for(i = 0; i < 360; i += 360 / slices) {
var element = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
element.setAttribute("points", "0,0 -10,100 10,100");
element.setAttribute("transform", "rotate(" + i + ")");
$('#rotate').append(element);
}

Thank you to much #Erik Dahlström, I may add a contribution to this. Here is the function I define in order to build svg trees :
Node.prototype.svg_grow = function(node_name, node_attr) {
n = document.createElementNS("http://www.w3.org/2000/svg", node_name);
this.appendChild(n);
if (typeof node_attr !== 'undefined') {
for (key in node_attr) {
n.setAttribute(key, node_attr[key]);
}
}
return n;
}
So you can just use svg_grow() on any node like this :
s_g = document.getElementById("parent");
s_rect = s_g.svg_grow('rect', {x:0, y:0, width:480, height:640});
which just do :
<g id="parent">
<rect x="0" y="0" width="480" height="640" />
</g>

Related

How to change bg color of dynamic CSS class from JS

I have a bar chart and I want to be able to assign individual fill colors to bars depending on a condition. I don't think it matters, but I am using angular-nvd3 linePlusBarChart to draw bars.
UPDATE
There seemingly is an issue with my local environment, possibly with NVD3 or angular libraries, I am not sure what's going on yet. I put together jsfiddle and everything worked perfectly. See the link below, the bars can have different colors.
On my local computer though, when the execution reaches the point var y = document.querySelector(...., I am getting angular error in console ->>>>> y is null.
http://jsfiddle.net/p0g9Lqu8/
Here is the HTML.
<nvd3>
<g class="nv-bars">
<rect x="0" y="419" height="1" fill="LimeGreen" class="nv-bar positive nv-bar-0-0" width="267"></rect>
<rect x="0" y="252" height="168" fill="LimeGreen" class="nv-bar positive nv-bar-0-1" width="267"></rect>
<rect x="0" y="294" height="126" fill="LimeGreen" class="nv-bar positive nv-bar-0-2 hover" width="267"></rect>
<rect x="0" y="252" height="168" fill="LimeGreen" class="nv-bar positive nv-bar-0-3" width="267"></rect>
</g>
</nvd3>
As you can see, each rect tag has a dynamic CSS class nv-bar-0-1, nv-bar-0-2.. that I would like to be able to point to from JS. NOTE: from HTML, if I use CSS, all works, but from JS loop, does not. Any help is appreciated.
This perfectly works.
<style>
nvd3 .nv-bars rect {
fill:white;
}
nvd3 .nv-bars rect.nv-bar-0-1{
fill:yellow;
}
</style>
But this, does not:
for(var i = 0; i < 4; i++) {
var y = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-' + i);
y.style.fill = "#ffff00";
}
OR this one, same result
var y = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-1); // y is always null
y.style.fill = "#ffff00"; // triggers null reference error here
I call that JS fragment inside Angular controller in this block:
angular.element(document).ready(function () {
var y = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-1');
console.log('>>>>>>>>>>>===' + y); // this outputs [object HTMLUnknownElement]
y.style.fill = "#ffff00";
});
I thank everybody for time and input. I finally found where the problem was and what the solution would be, so I would like to share it with the community in case anybody comes across a similar problem.
My final output into the browser is a result of combining multiple HTML/JS/CSS fragments. The issue was in timing: the JS code was attempting to refer to a tag, but it appeared the dynamic construction of that tag with its HTML/CSS was not complete at the time of JS reference.
angular.element(document).ready(.... could not do the job, but a pure JS approach from the link below - did.
https://github.com/jfriend00/docReady
I enclosed the JS block that was to check some conditions and assign colors to bars, into the docReady block. See the simplified code sample below.
docReady(function () {
for (var i = 0; i < 4; i++) {
var k = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-' + i);
// some condition goes here.....
k.style.fill = "green";
}
var y = document.querySelector('nvd3 .nv-bars rect.nv-bar-0-1');
// this too changes color
y.style.fill = "#ff0000";
});
Worked in all major browsers: Firefox, Chrome, Safari and IE

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.

SVG HTML Not Drawing SVG Pattern

Im creating an SVG pattern in HTML and the user will be able to change colour and size etc. But its not working.
I get an error regarding the body onload function. and then when appending the SVG Diagram to the svg placeholder that i have.
Here is the Script:
<script>
var SVG = document.getElementById("svgArea");
document.content.appendChild(SVG);
function drawCircle()
{
var svgLink = "http://www.w3.org/2000/svg";
var Centre = document.createElementNS(svgLink,"circle");
Centre.setAttributeNodeNS(null,"id","Centre");
Centre.setAttributeNodeNS(null,"cx",230);
Centre.setAttributeNodeNS(null,"cy",0);
Centre.setAttributeNodeNS(null,"r",75);
Centre.setAttributeNodeNS(null,"fill","centreColour");
document.getElementById("svgArea").appendChild(Centre);
var group = document.getElementById("svgArea");
group.setAttribute("transform","translate(230,0)");
}
</script>
Then for the body tag i have the following:
<body onload="drawCircle()">
And for the contents of the page i have the following code:
<div class="content">
<!-- SVG DIAGRAM -->
<svg id="SVG" style="background:pink" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="690" height"400">
<g id="svgArea">
</g>
</svg>
</div>
Errors:
[Error] TypeError: 'undefined' is not an object (evaluating 'document.content.appendChild')
global code (SVG.html, line 33)
[Error] TypeMismatchError: DOM Exception 17: The type of an object was incompatible with the expected type of the parameter associated to the object.
drawCircle (SVG.html, line 57)
onload (SVG.html, line 107)
Where and what am i doing wrong?
Thanks
This doesnt Work:
//Drawing Petals
for (var i = 0; i < numberOfPetals; i++)
{
var svgLink = "http://www.w3.org/2000/svg";
var flowerPettle = document.createElementNS(svgLink,"ellipse");
flowerPettle.setAttributeNS(null,"id","flowerPettle");
flowerPettle.setAttributeNS(null,"ry", 230);
flowerPettle.setAttributeNS(null,"rx",0)
flowerPettle.setAttributeNS(null,"fill",petalColour);
var rotate = "rotate(" + (i*(360 / numberOfPetals)) + " " + 300 + "," + 30 + ")";
flowerPettle.setAttribute("transform",rotate);
document.getElementById("FlowerArea").appendChild(flowerPettle);
}
There are a few things wrong with your code. One is the createAttributeNode() calls. Another is the document.content reference. Plus there are things like the strange colour value "centreColour".
I've put together a working fiddle. Hopefully it helps you get things working in your code.
Demo here

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>

Categories

Resources