How to edit svg attribute with javascript? - javascript

<svg width="100", height=100">
<polyline id="line" onclick="Click()" points="0,0 50,50" stroke-width="1" stroke="black"/>
</svg>
<script>
var line = document.getElementById('line');
line.style.points="0,50 0,0";
</script>
I wish to use javascript to generate images in page. For some reason this code has no effect. However if instead the last line was for example line.style.stroke="green";, it works. Why?

Related

Why doesn't my dynamically created <use> element show?

With JavaScript and jQuery I'm trying to replace a group element with a use element linking to another group element.
// Javascript
origgroup = $("#origgroup")[0];
repgroup = $("#referenceGroup1")[0];
origgroupParent = origgroup.parentNode;
use = document.createElementNS("http://www.w3.org/2000/svg", "use");
use.setAttribute("xlink:href", "#origgroup2");
use.setAttribute("id", "newuse");
tmp = origgroupParent.replaceChild(use, origgroup);
// After this snippet is run, "targetsvg" and "control" are identical. Except that targetsvg's use-tag has an unique ID.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- My "atlas". I want to put <use> elements in "targetsvg" below, linking to these groups. -->
Atlas <br>
<svg id="atlas" width="120" height="70" version="1.1">
<g id="referenceGroup1">
<rect x="10" y="10" width="90" height="20" fill="green"/>
<circle cx="20" cy="40" r="15" fill="blue"/>
</g>
<g id="referenceGroup2">
<rect x="40" y="10" width="90" height="20" fill="red"/>
<circle cx="50" cy="40" r="15" fill="orange"/>
</g>
</svg>
<br> Target <br>
<!-- My target -->
<svg id="targetsvg" width="120" height="70" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="origgroup">
<rect x="40" y="10" width="90" height="20" fill="red"/>
<circle cx="50" cy="40" r="15" fill="orange"/>
</g>
</svg>
<br>
Control
<br>
<!-- This is identical to the javascript modified version of "targetsvg" -->
<svg id="control" width="120" height="70" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#referenceGroup1"></use>
</svg>
What I expect to happen is for there to be a blue circle and a green rectangle under "Target". If I inspect the resulting svg of "targetsvg" it's identical to the svg under "Control". This leads me to believe that "targetsvg" is not redrawn for some reason, is this correct? Is there a way to force that?
I've spent the last five hours searching, but I can't find anything similar. The most relevant I've found is
SVG <use> in Chrome doesn't work
But that uses Angular, which I'm not. I think the cause is the same ("the relative hash link in the element would not correctly resolve."). But if this is the case, how do I resolve this without Angular?
Thanks!
[Background: I have a huge svg-file generated from illustrator. In this file there are a number of fairly complex elements (groups of groups et.c.) that I need to have different versions of. These elements will need to appear on multiple places in the final result, so I either need to have multiple copies of them (Showing/hiding depending on the situation) or some kind of 'atlas' where I pick and replace. My gut says the latter will be more maintainable since there are at least four places and seven "versions" (Think "green", "green with symbol x", "red with symbol y" et.c.). If there are other options, I welcome those.]
Minutes after posting, I realized it was a namespace problem. Changing the JavaScript to:
origgroup = $("#origgroup")[0];
repgroup = $("#referenceGroup1")[0];
origgroupParent = origgroup.parentNode;
// Namespaces
var svgns = 'http://www.w3.org/2000/svg',
xlinkns = 'http://www.w3.org/1999/xlink'
use = document.createElementNS(svgns, "use");
// **setAttributeNS** instead of setAttribute as originally.
use.setAttributeNS(xlinkns, "xlink:href", "#referenceGroup1");
use.setAttribute("id", "newuse");
tmp = origgroupParent.replaceChild(use, origgroup);
Solved my problem.

Changing Clickable Area of an Image without Resizing Image

I am currently working on a PHP/Javascript project where an action occurs when an image is clicked. The image is small, so I would like to expand the clickable area to further around the image without enlarging the image itself. Is this possible? Below is a general idea of the structure of what I'm working on.
<g id="pictures">
<image id="marker_image" cx="145" cy="460" r="1" preserveAspectRatio="none"
x="136" y="451" width="18" height="18"
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="//link.thislink.com/image_assets/markers/pool.png"
style="opacity: 1" fill="#000000" fill-opacity="1" stroke="#000000"
stroke-opacity="1" stroke-width="2" stroke-linecap="round" stroke-
linejoin="round"></image>
</g>
I'm new to working with SVGs, but from what I've read, I think I can use the <g> to add padding?
Wrap you image in a div, make the div as big as you want using padding, and bind your action on the div instead of the img

SVG: <use> element not working when nested in g (with fiddle!)

I need to display use elements in my SVG graphic.
When I try to use them from within a g element it doesn't work.
In a little demo it looks like the use element works outside of g elements.
Here the fiddle (you can scroll down to the use elements to see the demo):
http://jsfiddle.net/3dacnxdb/2/
Why is it like this? How can i display them from within a g element?
(My graphic is made out of many modules I need the gs to seperate them)
I appreciate any help!
<svg id="graphic">
<defs>
<clipPath id="icon-cp">
<rect x="0" y="0" width="150" height="100" />
</clipPath>
<image id="icon-sprite" width="969" height="293"
xlink:href="http://i.stack.imgur.com/TPx5h.png" />
<g id="icon2" clip-path="url(#icon-cp)">
<use xlink:href="#icon-sprite" transform="translate(-240,0)" />
</g>
</defs>
<!-- ----------------------------------------
Here is the question:
the first use element is not displayed.
The second one is displayed. (the elements are under this text)
Why does it not work?
How can a use element be used within nested g elements?
(to test it you can comment out the second use element,
even tough both use elements have the same attributes
no icon is visible anymore)
---------------------------------------- -->
<!-- following does not work: -->
<g id="testg">
<use xlink:href="#icon2" x="100" y="50" />
</g>
<!-- following works: -->
<use xlink:href="#icon2" x="100" y="50" />
<!-- why???? -->
It does work. Your jsfiddle is not the same as your inline code (the x and y of the use are not the same).
If you make the x and y values in the <g> the same as outside it will work. In the non-working case you are clipping out the contents of the <use> with your clip-path.
You can't see it because they are superposed... =)
<svg id="graphic">
<defs>
<clipPath id="icon-cp">
<rect x="0" y="0" width="150" height="100" />
</clipPath>
<image id="icon-sprite" width="969" height="293" xlink:href="http://i.stack.imgur.com/TPx5h.png" />
<g id="icon2" clip-path="url(#icon-cp)">
<use xlink:href="#icon-sprite" transform="translate(-240,0)" />
</g>
</defs>
<!-- ----------------------------------------
Here is the question:
the first use element is not displayed.
The second one is displayed. (the elements are under this text)
Why does it not work? How can a use element be used within nested g elements?
(to test it you can comment out the second use element, even tough both use elements have the same attributes no icon is visible anymore)
---------------------------------------- -->
<!-- following does not work: -->
<g id="testg">
<use xlink:href="#icon2" x="0" y="0" />
</g>
<!-- following works: -->
<use xlink:href="#icon2" x="100" y="50" />
<!-- why???? -->

Toggle layer in SVG

I am trying to create a HTML page with a check list that we can also make it large without losing quality. As a result I want to use SVG.
I would like to have a script to operate on the SVG so that I can toggle the group svg_2 (a check mark) on or off so that we have checked and unchecked box. It doesn't have to change when loaded, just need like an inline command that will do it.
<svg width="20%" height="20%" xmlns="http://www.w3.org/2000/svg">
<rect id="svg_1" fill="#ffffff" stroke="#000000" stroke-width="10%" x="2.5%" y="2.5%" width="85%" height="85%" />
<g id="svg_2">
<line fill="none" stroke="#ff0000" stroke-width="10%" x1="43.5%" y1="77.5%" x2="10.5%" y2="49.5%" id="svg_3" stroke-linecap="round" stroke-linejoin="bevel"/>
<line fill="none" stroke="#ff0000" stroke-width="10%" x1="95%" y1="9.5%" x2="44.5%" y2="78.5%" id="svg_4" stroke-linecap="round" stroke-linejoin="bevel"/>
</g>
</svg>
You can use JavaScript to toggle the svg_2 on or off depending on its previous state (example using JQuery):
$("svg").click(function() {
if ( $('#svg_2').css('visibility') == 'hidden' )
$('#svg_2').css('visibility','visible');
else
$('#svg_2').css('visibility','hidden');
});
You could also use some other CSS attribute (such as display).
See and try it here: JSFiddle

Call svg javascript function inside html javascript function

I have a SVG document where three circles are drawn:
<?xml version="1.0"?>
<svg width="450" height="80" xmlns="http://www.w3.org/2000/svg">
<script>
document.fillCircle = function(id) {
var circles = document.getElementsByTagName('circle'),
circle = document.getElementById(id);
[].forEach.call(circles, function(circle) {
circle.setAttribute('fill','#ffffff');
});
circle.setAttribute('fill', '#000000');
}
</script>
<g>
<line y1="35" x1="35" y2="35" x2="375" stroke-width="3" stroke="#000000"/>
<circle id="state1" r="30" cy="35" cx="35" stroke-width="3" stroke="#000000" fill="#ffffff" onclick="fillCircle(this.id);"/>
<circle id="state2" r="30" cy="35" cx="205" stroke-width="3" stroke="#000000" fill="#ffffff" onclick="fillCircle(this.id);"/>
<circle id="state3" r="30" cy="35" cx="375" stroke-width="3" stroke="#000000" fill="#ffffff" onclick="fillCircle(this.id);"/>
</g>
</svg>
For testing purposes I have the onclick="" method, but actually this document is an object in my html document:
<object id="test" data="test-vector.svg" width="100px" height="100px"></object>
I have a dataset and these three circles show the "progress" of every item. I regularly update the JSON set by pulling the new list from the server. For every item changed, I want to update the filled circle.
I would like to update the svg based on some javascript. However, I can't make it to get into the DOM of the SVG. I do not really care if the fillCircle() is inside the svg or not and if I have to use <embed>, <object> or something else, but this kind of javascript does not work for me.
<html>
<body>
<object id="test" data="test-vector.svg"></object>
<script>
var svg = document.getElementById('test');
console.log(svg);
svg.fillCircle('state2');
</script>
</body>
</html>
I tried several things I found on SO, like this one and this one, but whatever I test, the exception is always:
Uncaught TypeError: Object #<HTMLObjectElement> has no method 'fillCircle'
var object = document.getElementById("test") will get you the object element but you can't call that till the object has loaded. Once you have that you can use object.contentDocument to do things with the embedded svg document.
<html>
<body>
<object id="test" data="test-vector.svg" onload="f()" ></object>
<script>
function f() {
var svg = document.getElementById('test');
svg.contentDocument.fillCircle('state2');
}
</script>
</body>
</html>
Why don't you embed the SVG directly in your HTML code (using SVG tags)? According to W3, this works in all modern browsers (and IE >= 9). Accessing and changing the circles' properties with JS is then trivial...
<html>
<body>
<svg>...</svg>
</body>
</html>
If you want to keep your HTML/SVG structure though, you can do the following:
var svg = document.getElementById("test");
svg.onload = function(){
svg.contentDocument.fillCircle("state2");
};
The trick is to wait for the SVG object to load (onload event); not till then you can safely use the contentDocument property. Btw, this is also described in this solution on SO (you posted a link to it). ;)

Categories