d3.js: insert g elements at a certain location - javascript

For example if i have this
<div>
<g class="one"></g>
<g class="two"></g>
<g class="three"></g>
<g class="four"></g>
<g class="five"></g>
</div>
If i want to insert another g with class new in between the the two g with class three and two.
Resulting in
<div>
<g class="one"></g>
<g class="two"></g>
<g class="new"></g>
<g class="three"></g>
<g class="four"></g>
<g class="five"></g>
</div>
These work:
d3.select("div").insert("g", ".three").attr("class", "new");
d3.select("div").insert("g", ":nth-child(3)").attr("class", "new");
But it will not work if i have this and want to insert a g with class new at where the line is. (I've used lines as I'm not sure if i will explain it correctly)
<div>
<g class="one">
<g class="two">
<circle></circle>
</g>
<g class="one">
<circle></circle>
</g>
<g class="one">
<circle></circle>
</g>
</g>
<g class="one"></g>
------------------(this is where i want <g class="new"></g>)--------
<g class="two"></g>
<g class="one"></g>
<g class="one"></g>
</div>
Is it possible to insert it by only counting the number of g at the level under div. E.g between the 2nd and 3rd g tag.
I've tried something like:
d3.select("div").insert("g", "div > g, g:nth-of-type(3)").attr("class", "new");
I figured out a workaround:
d3.selectAll("g").filter("div > g").classed("temp", true);
d3.select("div").insert("g", ".temp:nth-of-type(3)").attr("class", "new");
d3.selectAll(".temp").classed("temp", false);
But it seems really inefficient.

The following should work for you:
d3.select("div").insert("g", ".three").attr("class", "new");
Here's a working fiddle.
If you wanted to insert after the nth element, rather than after a specific class, you can do something like:
d3.select("div").insert("g", ":nth-child(3)").attr("class", "new");
If you need even more flexibility (maybe you have other elements in that div other than g), you can insert before the nth g like so:
d3.select("div").insert("g", "g:nth-of-type(3)").attr("class", "new");
EDIT:
Still unclear what you're trying to accomplish, but here is my last attempt:
d3.select("div").insert("g", "div > g:nth-of-type(3)").attr("class", "new").append("text").text("new ");
Codepen
This will insert the new g before the third g element that is a direct child of the div. It has nothing to do with the classes, I don't know why you mentioned classes in the first place, especially if you knew they were not unique but your example makes it appear as if that were so.
You should read this.

Related

Draw svg element ontop of a nested element

Using either d3.js or a javascript method, is there a way to draw the nested circle (#snap-points) on top of the line group?
<g id='panel-group'>
<rect id='panel-rectangles' />
<circle id='snap-points' />
</g>
<g id='line-group>
<line/>
</g>
document.getElementById('snap-points').appendChild(document.getElementById('line-group'))
Should do it i.e. reparent the snap-points element as a child of the line-group.

Targeting elements that are not direct children in HTML using Javascript?

I am using Chartist.js for some charts and I need to target some elements inside the chart in order to generate some elements that are related to them. Is there a way to target such deeply nested child elements inside the "chart1". I need to target the "g" elements with the classes of "ct-series-a", "ct-series-b", etc... But since there are multiple charts on the page that ALSO have elements with these class names, I need to only target the ones that are contained in the chart with the id of "chart1".
HTML Mock-up
<div id="chart1" class="ct-chart">
<svg>
<g>
<g class="ct-series ct-series-a"></g>
<g class="ct-series ct-series-b"></g>
<g class="ct-series ct-series-c"></g>
</g>
</svg>
</div>
Somthing like this should help you.
const chart1 = document.getElementById("chart1");
const seriesA = chart1.getElementsByClassName("ct-series-a")[0]
console.log(seriesA.className)
// I did use className , for reducing console data to show u the result in console .
<div id="chart1" class="ct-chart">
<svg>
<g>
<g class="ct-series ct-series-a"></g>
<g class="ct-series ct-series-b"></g>
<g class="ct-series ct-series-c"></g>
</g>
</svg>
</div>

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.

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

Categories