SVG: Remove element from svg using javascript or jquery - javascript

I add a foreignObject insider g element in svg the html is as follow:
<g id="group" transform="matrix(1,0,0,1,3700,20)">
<rect x="80" y="80" stroke="black" id="occ" style="stroke-width: 10px; stroke: rgb(0, 128, 255);" width="140" height="140" fill="white" class="marked"></rect>
<foreignObject id="fo_1" x="29" y="29" width="18" height="18" transform="scale(4,4)"><div style="color: brown; stroke: rgb(0, 128, 255); stroke-width: 10px;" class="marked"><i class="fa fa-circle"></i></div></foreignObject>
</g>
I added the foreignObject when the rect is clicked with command:
element.parentElement.insertAdjacentHTML('beforeend', '<foreignObject ...>);
But then I fail to remove the foreignObject again if the rect is clicked again. At the moment, I have successfully find the foreignObject element by using ID, but still cannot remove it.
I tried the code like below:
$('svg').find('foreignObject#fo_1').remove();
or this code:
var foreignObj = document.getElementById('fo_1');
var parent = foreignObj.parentNode;
parent.removeChild(foreignObj);
Anyone can help please? Thanks.

Related

Is there a way I can fill different color inside SVG image using HTML (currently using d3.js)

I have a SVG which needs to be filled as per data just like this image. I need to fill arms with different color, head with different color and legs with different color, that all using HTML, CSS and JavaScript. I'm currenlty using it on Angular which also uses D3.js. Is there any work around of this?
You can just give the SVG some classes and toggle them using JS or edit the elements fill style.
const colors = ['red', 'blue', 'green', 'yellow', 'gray']
setInterval(() => {
document.getElementById('hair').style.fill = colors[Math.floor(Math.random() * colors.length)];
}, 200)
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" width="100px" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 511.999 511.999" style="enable-background:new 0 0 511.999 511.999;" xml:space="preserve">
<g>
<path id="hair" d="M410.439,0c0,0-18.211,44.138-141.241,44.138h-79.448c0,0-123.586-6.073-123.586,105.931v82.476
c0,31.974,12.703,62.632,35.31,85.248h300.138l6.294-6.294c19.474-19.482,31.921-45.047,34.648-72.466
C458.629,77.674,410.439,0,410.439,0"/>
<path style="fill:#FDD7AD;" d="M101.473,194.207c0-7.318,1.474-14.292,4.158-20.63c10.099-23.905,38.391-33.66,62.791-24.832
c13.294,4.811,38.179,10.152,83.121,10.152s69.826-5.341,83.121-10.152c24.399-8.828,52.683,0.927,62.791,24.832
c2.675,6.347,4.158,13.312,4.158,20.63l0.238,80.446c0,55.428-4.317,116.401-46.133,156.089
c-14.989,14.239-32.653,25.203-49.717,36.776c-17.064,11.564-32.106,26.827-54.219,26.827s-37.155-15.263-54.219-26.827
c-17.055-11.573-34.719-22.537-49.717-36.776c-41.807-39.689-46.371-101.667-46.371-157.087V194.207z"/>
</svg>
Since SVGs are just markup that can be written directly into HTML, they can also have attributes such as classes and IDs, so you can just style them like you would regular elements.
You can also target them with JS to add/remove attributes etc.
Here is a super simple example of a styled SVG to illustrate:
#rect-1 {
fill: red;
}
#rect-2 {
fill: green;
}
#rect-3 {
fill: blue;
}
#rect-4 {
fill: yellow;
}
<svg>
<rect id="rect-1" x="0" y="0" width="10" height="10" />
<rect id="rect-2" x="0" y="20" width="10" height="10" />
<rect id="rect-3" x="20" y="20" width="10" height="10" />
<rect id="rect-4" x="20" y="0" width="10" height="10" />
</svg>

Position text element at left side of parent svg

Inside my SVG elment is a g element which contains several rect, circle & text nodes.
<svg id="familyTreeSvg" xmlns="http://www.w3.org/2000/svg">
<g id="familyTreeSvgG" transform="translate(-3640.998879446216,-3805.319191946216) scale(1)">
<rect class="lifeline" x="3999.329665532318" y="3911.819191946216" width="441.67051879637665" height="20"
rx="10"
ry="10" id="lifelineRectSamanthaBishopI10" style="fill: red; opacity: 1;"></rect>
<rect class="lifeline" x="3981.819191946216" y="3986.819191946216" width="459.1809923824785" height="40" rx="10"
ry="10" id="lifelineRectOliverLionI1" style="fill: rgb(255, 255, 255); opacity: 1;"></rect>
<g class="name">
<text x="4006.819191946216" y="4011.819191946216" style="fill-opacity: 1;">Oliver Lion</text>
</g>
<g class="name">
<text x="4024.329665532318" y="3926.819191946216" style="fill-opacity: 1;">Samantha Bishop</text>
</g>
<g class="node M" id="rootNode" transform="translate(3991.819191946216,4006.819191946216)">
<circle class="node" r="20" style="fill: rgb(255, 255, 255);"></circle>
</g>
<g class="node F" id="SamanthaBishopI10" transform="translate(4009.32958984375, 3921.819091796875)">
<circle class="node" r="13" style="fill: rgb(255, 255, 255);"></circle>
</g>
</g>
</svg>
I use D3js to zoom and drag the g-element.
When a name (text-element) would translate outside the left side I want it to stick to the left side rather than translating to the left, so that the name is still visible.
My current attempt is:
function movePersonNames(person, lifeLine) {
let boundingBox = lifeLine.getBoundingClientRect();
let lifeLineWidth = boundingBox.width;
let lifeLineCoordinate = boundingBox.x;
let lifeLineVisible = lifeLineWidth + lifeLineCoordinate;
if (lifeLineCoordinate < 0 && lifeLineVisible > 0) {
let moveText = "translate(" + lifeLineCoordinate * -1 + ",0)";
d3.select(person).attr("transform", moveText);
}
}
but than some names are still partly invisible, while others are too far away:
It's even worse when the scale is not 1.
I also tried to use the inverted d3.event.transform.x:
d3.select(person).attr("transform", "translate(" + d3.event.transform.x * -1 + ",0)");
but than all the names jump too far and are no longer visible.
That's how i want it:
How can I achieve this?
Let me know if you need further information.

Whats the correct way to get the bounding box of DOM elements containing svg

I have the following html and get strange results with getBoundingClientRect, if there are svg elements inside:
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<svg>
<g transform="translate(10,10) scale(1)">
<g class="nodes">
<g style="inline" transform="translate(20,20)">
<rect style="stroke: rgb(170, 170, 170); stroke-width: 1; fill: rgb(248, 248, 248);" width="100" height="90"></rect>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(182, 204, 216); stroke-width: 0;" x="0" y="0" height="20" width="100"></rect>
<text class="nodetext" x="3" y="15">Text 1</text>
</g>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(221, 185, 172); stroke-width: 0;" x="0" y="22" height="20" width="100"></rect>
<text class="nodetext" x="3" y="37">Test 2</text>
</g>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(221, 185, 180); stroke-width: 0;" x="0" y="44" height="20" width="100"></rect>
<text class="nodetext" x="3" y="59">Test 3</text>
</g>
<g class="nodebox">
<rect class="noderect" style="fill: rgb(236, 163, 154); stroke: rgb(212, 139, 130); stroke-width: 2;" x="0" y="66" height="20" width="100"></rect>
<text class="nodetext" x="3" y="81">Test 4</text>
<g class="nodeicon">
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" x="80" y="68">
<rect height="14" width="14" y="1" x="1" stroke="#888888" stroke-width="1" fill="#ffffff" fill-opacity="0.5"></rect>
<line x1="4" y1="8" x2="12" y2="8" stroke="#888888" stroke-width="2"></line>
</svg>
</g>
</g>
</g>
</g>
</g>
</svg>
</body>
</html>
I get a much greater rectangle than I would expect in Firefox.
When I inspect the objects, the displayed bounding box for the inner svg element is fine, but the surrounding g element (class nodeicon) is outside.
If I remove this g element, the next surrounding g element is outside.
The following picture shows this:
It looks like the offset of the svg is applied twice.
Is getBoundingClientRect the correct way to get the position and size of elements (e.g. the g element with class nodes) for this? Is there something wrong with the HTML or svg element or did I run into a Firefox bug?
I am using the current version of Firefox (58.0 64bit).
A problem that you have here is that the svg tag nested inside the g tag (.nodeicon) is starting a new viewport context. Strictly speaking it should not be nested inside a g tag anyway, but regardless, it isn't really necessary as you're using it as a method of grouping the two elements inside it - which is the purpose of the g tag.
Try removing the svg tag nested inside .nodeicon, and move the coordinates in that svg's x and y attributes to a transform attribute on the g tag.
i.e:
<g class="nodeicon" transform="translate(80, 68)">
<rect height="14" width="14" y="1" x="1" stroke="#888888" stroke-width="1" fill="#ffffff" fill-opacity="0.5"></rect>
<line x1="4" y1="8" x2="12" y2="8" stroke="#888888" stroke-width="2"></line>
</g>

d3 change class of clicked node text

I'm trying to add a class to the label, that was clicked in my force diagram. For this I use a "click" function, which hands the clicked item to the update function (it updates the class of the item). I tried using this but then the console will report "nodeClicked.childNodes[1].classed is not a function".
Then I googled and tried to use "d3.select(this).select("text");" which does not report an error, nor update the text.
A node is a < g > Element which has two children: a circle and the text (the text I want to give a css class)
If you want to, you can look at my code snippet:
// Toggle children on click.
// Also save information about what was clicked and update the GUI
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
}
else{
d.children = d._children;
d._children = null;
}
// Save which node was just clicked now
nodeClicked = d3.select(this).select("text");
// Save the d3 data of this node
nodeClickedData = d;
}
To change the class I tested both:
$(nodeClicked).addClass("nodeGreenMarked");
and:
nodeClicked.classed("nodeBlueMarked", true);
But none did anything. If I check in the console what content nodeClicked is, it tells me "Array[1]" and when I open it: "0:< text >"
You can add class in two ways.
d3.select(this).select("text").attr("class",className);
OR
d3.select(this).select("text").classed(className,true);
Working Snippet:
d3.selectAll(".node").on("click", function() {
//check if node is already selected
var text = d3.select(this).select("text");
if (text.classed("selectedText")) {
text.classed("selectedText", false);
//Remove class selectedNode
} else {
text.classed("selectedText", true);
//Adds class selectedNode
}
});
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
.selectedText {
fill: red;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<svg width="960" height="500">
<g transform="translate(120,20)">
<path class="link" d="M0,262.85714285714283C90,262.85714285714283 90,197.1428571428571 180,197.1428571428571"></path>
<path class="link" d="M0,262.85714285714283C90,262.85714285714283 90,328.57142857142856 180,328.57142857142856"></path>
<path class="link" d="M180,197.1428571428571C270,197.1428571428571 270,131.42857142857142 360,131.42857142857142"></path>
<path class="link" d="M180,197.1428571428571C270,197.1428571428571 270,262.85714285714283 360,262.85714285714283"></path>
<g class="node" transform="translate(180,328.5714416503906)">
<circle r="10" style="fill: rgb(255, 255, 255);"></circle>
<text x="13" dy=".35em" text-anchor="start" style="fill-opacity: 1;">Level 2: B</text>
</g>
<g class="node" transform="translate(180,197.14285278320312)">
<circle r="10" style="fill: rgb(255, 255, 255);"></circle>
<text x="-13" dy=".35em" text-anchor="end" style="fill-opacity: 1;">Level 2: A</text>
</g>
<g class="node" transform="translate(0,262.8571472167969)">
<circle r="10" style="fill: rgb(255, 255, 255);"></circle>
<text x="-13" dy=".35em" text-anchor="end" style="fill-opacity: 1;">Top Level</text>
</g>
<g class="node" transform="translate(360,262.8571472167969)">
<circle r="10" style="fill: rgb(255, 255, 255);"></circle>
<text x="13" dy=".35em" text-anchor="start" style="fill-opacity: 1;">Daughter of A</text>
</g>
<g class="node" transform="translate(360,131.42857360839844)">
<circle r="10" style="fill: rgb(255, 255, 255);"></circle>
<text x="13" dy=".35em" text-anchor="start" style="fill-opacity: 1;">Son of A</text>
</g>
</g>
</svg>
D3 did somehow not correctly address the correct element (the text of the group element), no matter which of the possible methods I used (jQuery did somehow also fail). I must have keeping to oversee something really important, though I checked everything like 5 times.
No matter, I found a working solution: by adressing the DOM elements with pure javascript. The command I used to solve this and get the class applied correctly was
nodeClicked = this;
...
nodeClicked.classList.add("greenNode");
This adds the class greenNode to the group element. In my CSS file I wrote .nodeClicked text and put in the necessary CSS. So if you once also have problems addressing the correct element, you can also try to use this method.

Accessing SVG file directly from Javascript code

I have this HTML code, which is invoking my javascript code. The code is for a gauge. In the javascript code, I am trying to access a SVG file, and modifying the needle (of the gauge) to display the desired value. The code is working fine. However, I do not wish to call "object id" in HTML. I want to access SVG file through javascript directly, instead of using object id in HTML. I tried using el.setAttribute('data', 'gauge.svg'); But then svg_doc isn't able to retrieve the SVG image and modify the needle. Any help would be highly appreciated.
PS : I tried my best to be as thorough in explaining the problem. However, please let me know if I am unclear somewhere.
This is Gauge.png image which is embedded in the svg code I have pasted below https://sphotos-b.xx.fbcdn.net/hphotos-snc6/179594_10150982737360698_1827200234_n.jpg
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g name="gauge" width="122px" height="127px">
<image xlink:href="gauging.png" width="122" height="127"/>
<circle id="led" cx="39" cy="76" r="5" style="fill: #999; stroke: none">
<animateColor id="ledAnimation" attributeName="fill" attributeType="css" begin="0s" dur="1s"
values="none;#f88;#f00;#f88;none;" repeatCount="0"/>
</circle>
<g id="needle" transform="rotate(0,62,62)">
<circle cx="62" cy="62" r="4" style="fill: #c00; stroke: none"/>
<rect transform="rotate(-130,62,62)" name="arrow" x="58" y="38" width="8" height="24" style="fill: #c00; stroke: none"/>
<polygon transform="rotate(-130,62,62)" points="58,39,66,39,62,30,58,39" style="fill: #c00; stroke: none"/>
</g>
<text id="value" x="51" y="98" focusable="false" editable="no" style="stroke:none; fill:#fff; font-family: monospace; font-size: 12px"></text>
</g>
</svg>
HTML+Javascript code
<head>
<title>SVG Gauge example</title>
<script>
function update1(){
var scale=100;
var value;
var value1 = 69;
var el=document.getElementById('gauge1');
if (!el) return;
/* Get SVG document from HTML element */
var svg_doc = el.contentDocument;
if (!svg_doc) return;
/* Rotate needle to display given value */
var needle_el = svg_doc.getElementById('needle');
if (!needle_el) return;
/* Calc rotation angle (0->0%, 260->100%) */
value = parseInt(value1);
scale = parseInt(scale);
if (value > scale) value = scale;
var angle = value / scale * 260;
/* On-the-fly SVG transform */
needle_el.setAttribute('transform','rotate('+angle+',62,62)');
}
document.addEventListener('load', update1, true);
</script>
</head>
<div>
<object id="gauge1" type="image/svg+xml" data="gauge.svg" width="127" height="122"/>
</div>
</html>
As robertc already mentioned, you can embed the javascript code into your SVG file:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g name="gauge" width="122px" height="127px">
<image xlink:href="gauging.png" width="122" height="127"/>
<circle id="led" cx="39" cy="76" r="5" style="fill: #999; stroke: none">
<animateColor id="ledAnimation" attributeName="fill" attributeType="css" begin="0s" dur="1s"
values="none;#f88;#f00;#f88;none;" repeatCount="0"/>
</circle>
<g id="needle" transform="rotate(0,62,62)">
<circle cx="62" cy="62" r="4" style="fill: #c00; stroke: none"/>
<rect transform="rotate(-130,62,62)" name="arrow" x="58" y="38" width="8" height="24" style="fill: #c00; stroke: none"/>
<polygon transform="rotate(-130,62,62)" points="58,39,66,39,62,30,58,39" style="fill: #c00; stroke: none"/>
</g>
<text id="value" x="51" y="98" focusable="false" editable="no" style="stroke:none; fill:#fff; font-family: monospace; font-size: 12px"></text>
</g>
<script type="text/javascript">
var scale=100;
var value;
var value1 = 69;
/* Rotate needle to display given value */
var needle_el = document.getElementById('needle');
/* Calc rotation angle (0->0%, 260->100%) */
value = parseInt(value1);
scale = parseInt(scale);
if (value > scale) value = scale;
var angle = value / scale * 260;
/* On-the-fly SVG transform */
needle_el.setAttribute('transform','rotate('+angle+',62,62)');
</script>
</svg>
I've put the code below the actual SVG contents so that the document is already loaded when the script is executed.
Then, you can view the SVG file directly e.g. in Firefox (I've tested it right now).

Categories

Resources