How to use enter(), exit() and update with horizontal "stacked" chart - javascript

I have a pseudo-stacked, horizontal chart. I saw pseudo because it mimics a stacked chart but is not really multiple stacked values, but rather 3 different values that add up to a total of 100%. I'm not sure what the name of this kind of chart is.
Anyways, I'm trying to figure out how to get the enter() and exit() and updating to work for the chart when the data changes. I have this jsfiddle as an example
http://jsfiddle.net/Uhrgt/
The svg structure starts like this:
<svg class="chart" width="756" height="50">
<g transform="translate(0,0)" width="73.6">
<rect height="50" width="224" style="fill: rgb(0, 0, 255);"></rect>
<text x="113" y="25" dy=".35em">30%</text>
</g>
<g transform="translate(75,0)" width="451">
<rect height="50" width="451" style="fill: rgb(255, 0, 0);"></rect>
<text x="226" y="25" dy=".35em">60%</text>
</g>
<g transform="translate(529.2000122070312,0)" width="224.8">
<rect height="50" width="73" style="fill: rgb(128, 0, 128);"></rect>
<text x="37" y="25" dy=".35em">10%</text>
</g>
</svg>
And after the data changes, it looks like this:
<svg class="chart" width="756" height="50">
<g transform="translate(0,0)" width="73.6">
<rect height="50" width="224" style="fill: rgb(0, 0, 255);"></rect>
<text x="113" y="25" dy=".35em">30%</text>
<rect height="50" width="73" style="fill: rgb(0, 0, 255);"></rect>
<text x="37" y="25" dy=".35em">10%</text>
</g>
<g transform="translate(75,0)" width="451">
<rect height="50" width="451" style="fill: rgb(255, 0, 0);"></rect>
<text x="226" y="25" dy=".35em">60%</text>
<rect height="50" width="451" style="fill: rgb(255, 0, 0);"></rect>
<text x="226" y="25" dy=".35em">60%</text>
</g>
<g transform="translate(529,0)" width="224.8">
<rect height="50" width="73" style="fill: rgb(128, 0, 128);"></rect>
<text x="37" y="25" dy=".35em">10%</text>
<rect height="50" width="224" style="fill: rgb(128, 0, 128);"></rect>
<text x="113" y="25" dy=".35em">30%</text>
</g>
</svg>
The rects and texts are getting created again, and the old ones are not being removed. There are two problems here:
The rects are being duplicated for some reason. I'm not sure why the exit() isn't working for them.
The texts are being duplicated because I have text.exit().remove() commented out at the moment, but uncommenting it results in an error for some reason.
What the proper way to use the enter, update and exit functions for when you create a chart and then later update the data for them? I have been following many online examples and thought I was using the correct syntax but it's not working properly obviously.

The source of the issues you're seeing is that you have your text and rect elements grouped in g elements. You need to handle enter, update and exit selections only for the g elements. Everything else hangs off of that. This implies that you need to save the enter selection for the g elements and then append the other elements to that.
The structure would look like this:
var barEnter = bar.enter().append('g');
barEnter.append("rect");
barEnter.append("text");
For the update selection:
bar.transition()...
bar.select("rect").transition()...
bar.select("text").transition()...
For the exit selection, you just need to remove the g elements because that will also remove everything contained within them:
bar.exit().remove();
Complete demo here.

Related

Center elements inside SVG relative to each other-

Couldn't find it anywhere else to I might ask here :
I have svg rectangle which inside has other rectangles.
What I want to achieve is to center each of these rectangles in the centre of it's parent and in relation to each other.
If I drag rectangle 1 down I want the other one to move up to keep both of them centered - and same thing happening if I drag the other element down (should push upper one up).
Problem here is that I might have different width/heights and there would be 2 or more elements. Is there any mathematical equationfor that? Or a name that I can look for?
I would put the 3 rects inside a group and use the group as in the following example
svg{
border:1px solid;
width: 30vh;
}
<svg viewBox="0 0 100 280">
<defs>
<g id="rects">
<rect width="80" height="80" />
<rect width="60" height="25" x="10" y="10" />
<rect width="60" height="25" x="10" y="45" />
</g>
</defs>
<use xlink:href="#rects" x="10" y="10" stroke="black" fill="none" />
<use xlink:href="#rects" x="10" y="100" stroke="black" fill="none" />
<use xlink:href="#rects" x="10" y="190" stroke="black" fill="none" />
</svg>

Hover events for a dashed line in an SVG

An SVG with a line (or path) which uses stroke-dasharray only seems to trigger CSS and JS hover events when the user hovers over the solid parts of the dashed line: https://codepen.io/anon/pen/YeXoZy
Is there a simple way to make both the JS and CSS events trigger when the solid or invisible parts of the line are hovered?
My current plan is to draw a second, invisible line following the same path and use it to detect mouse events. https://codepen.io/anon/pen/BYNgRR This seems heavy handed and I'm hoping there's a cleaner way I'm missing.
I'm not sure how to do it without the second "detector" line, but a less heavy handed way is at least possible without the JS.
Switch the order of the lines, then you can use the hover selector as usual for the dashed line, then use + in a selector for the detector line to change the properties of the line immediately following it:
https://codepen.io/RyanGoree/pen/LQVKBV
This can be solved somewhat by using a rect instead of line and using SVG transforms with patterns.
An example can be seen at this CodePen.
It essentially bubbles down to:
<svg height="210" width="500">
<defs>
<pattern id="pattern1"
width="10" height="10"
patternUnits="userSpaceOnUse"
patternTransform="rotate(0 60 60)">
<line stroke="green" stroke-width="12px" y2="10"/>
</pattern>
<pattern id="pattern2"
width="10" height="10"
patternUnits="userSpaceOnUse"
patternTransform="rotate(0 60 60)">
<line stroke="red" stroke-width="12px" y2="10" stroke="transparent"/>
</pattern>
</defs>
<g transform="rotate(45 60 60)">
<rect x="0" y="0" width="500" height="5"/>
</g>
</svg>
And the following CSS:
rect {
fill: url(#pattern1)
}
rect:hover {
fill: url(#pattern2)
}
This is an old topic I know. But found the answer on Css hover sometimes doesn't work on svg paths
If you want to trigger the event only on stroke/visible, use pointer-events: stroke; or pointer-events: visible; (only the stroke) and pointer-events: all; (on both)
Here is a code example:
<body>
<div id="donut-score" class="svg-item" style="display: block;">
<svg width="100%" height="100%" viewBox="0 0 40 40" class="donut">
<circle class="donut-hole" cx="20" cy="20" r="15.91549430918954" fill="#fff"></circle>
<circle class="donut-ring" cx="20" cy="20" r="15.91549430918954" fill="transparent" stroke-width="3.5"></circle>
<circle id="donut-score-part-1" class="donut-segment donut-segment-1" onmousemove="this.style.stroke ='orange';" onmouseout="this.style.stroke = '#ff8197';" cx="20" cy="20" r="15.91549430918954" fill="transparent" stroke-width="3.5" stroke-dasharray="20 80" stroke-dashoffset="25"></circle>
<g class="donut-text-item donut-text-item">
<text y="50%" transform="translate(0, 2)">
<tspan id="donut-score-text" x="50%" text-anchor="middle" class="donut-text">Score </tspan>
<tspan id="donut-score-aantal" x="50%" Y="65%" text-anchor="middle" class="donut-text">0 </tspan>
</text>
</g>
</svg>
</div>
</body>
ccs:
.donut-segment {
stroke: #ff8197;
}
.svg-item {
width:200px;
font-size: 16px;
margin: 0 auto;
}
.donut-text {
font-size: 0.35em;
line-height: 1;
transform: translateY(0.5em);
font-weight: bold;
}
If you add pointer-events: stroke; to the class .donut-segment then it only works on the stroke. If you use non or pointer-events: all it works on both. I tested it in codepen.

Getting siblings of g element in SVG is returning all similar level elements from other SVGs on the page using jquery. Why?

I'm currently creating multiple svgs on a single page, where each svg usually has three buttons to click. Each button is a rect and text element grouped by a g element. I want a user to click a button, and set a border on that button to show the selection. This is currently being done by adding a .selected class to the chosen button and removing .selected from any sibling. When I try to query the siblings and remove the .selected class, I am somehow removing the class from other svgs on the page. I'm guessing it's because jquery and svg don't play nice, so I'm thinking of switching to an svg library instead, but until now I've been able to work around most issues cleanly. Code below:
<!-- Index.html -->
...
<svg ...//attributes//...>
<g class="button1">
<rect class="" ...//attributes//...></rect> // Clicking this one...
<text ...//attributes//...> button 1 </text>
</g>
<g class="button2">
<rect class="" ...//attributes//...></rect>
<text ...//attributes//...> button 2 </text>
</g>
</svg>
...
<svg ...//attributes//...>
<g class="button1">
<rect class="" ...//attributes//...></rect> // ...affects these elements
<text ...//attributes//...> button 1 </text>
</g>
<g class="button2">
<rect class="" ...//attributes//...></rect>
<text ...//attributes//...> button 2 </text>
</g>
</svg>
Jquery:
$("g").click(function () {
var selection = $(this);
var siblings = selection.siblings();
for (var sibling in siblings) {
$(sibling).find("rect").attr("class", "");
}
selection.find("rect").attr("class","selected");
});
I've also tried using closest("svg").children("rect"), closest("g").children("rect") after encompassing the all buttons in another g element, parent("g").children("rect"). All do the same thing.
EDIT: MCVE (I hope): https://jsfiddle.net/c1cceya9/
Your problem is this code:
for (var sibling in siblings) {
$(sibling).find("rect").attr("class", "");
}
You don't need to loop through the siblings yourself, jQuery will do that for you. All you need is:
$("g").click(function () {
var selection = $(this);
selection.siblings().find("rect").attr("class", "");
selection.find("rect").attr("class","selected");
});
Your code was not doing what you think it was. It was looping through the jQuery object, not an array of siblings. The jQuery object contains all sorts of fields including pointers to the document. That's why it was clearing all selections.
Working demo
$("g").click(function () {
var selection = $(this);
selection.siblings().find("rect").attr("class", "");
selection.find("rect").attr("class","selected");
});
.selected {
stroke: white;
stroke-width: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg height="100" width="200">
<rect height="100" width="200" fill="black"></rect>
<g class="button1">
<rect class="" x="10" y="10" height="80" width="80" fill="red"></rect> // Clicking this one...
<text x="25" y="50" fill="white"> button 1 </text>
</g>
<g class="button2">
<rect class="" x="110" y="10" height="80" width="80" fill="blue"></rect>
<text x="125" y="50" fill="white"> button 2 </text>
</g>
</svg>
<svg height="100" width="200">
<rect height="100" width="200" fill="black"></rect>
<g class="button1">
<rect class="" x="10" y="10" height="80" width="80" fill="red"></rect> // Clicking this one...
<text x="25" y="50" fill="white"> button 1 </text>
</g>
<g class="button2">
<rect class="" x="110" y="10" height="80" width="80" fill="blue"></rect>
<text x="125" y="50" fill="white"> button 2 </text>
</g>
</svg>

Use tag in svg's not working

I'm pretty new to SVG. I have set of icons created using SVG. I'm trying to use <use> tag to render a particular part of SVG. But everything goes in vain. Not able to figure out what's the mistake i'm doing. Here is the code which i tried and also refer the fiddle. You can see that overall svg is rendered, But trying to render particular part of the SVG failed. Any help will be appreciated
<svg width="303px" height="30px" viewBox="0 0 303 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Pivot" transform="translate(253.232000, 6.247500)">
<path d="M0.833,15.4105 L0.833,11.662 L3.7485,11.662 L3.7485,11.2455 L0.833,11.2455 L0.833,7.9135 L3.7485,7.9135 L3.7485,7.497 L0.833,7.497 L0.833,4.5815 L0,4.5815 L0,16.2435 L0.833,16.2435 L0.833,15.827 L3.7485,15.827 L3.7485,15.4105 L0.833,15.4105 Z M18.8036328,16.2435 L4.5815,16.2435 L4.5815,15.6124537 L8.7465,15.6124537 L8.7465,12.7095463 L4.5815,12.7095463 L4.5815,12.0785 L8.7465,12.0785 L8.7465,8.12804634 L4.5815,8.12804634 L4.5815,7.497 L8.7465,7.497 L8.7465,4.5815 L3.7485,4.5815 L3.7485,4.4408921e-16 L19.5755,4.4408921e-16 L19.5755,4.5815 L17.3516962,4.5815 L14.5775,4.5815 L14.5775,7.497 L18.7425,7.497 L18.7425,4.5815 L19.5755,4.5815 L19.5755,16.2435 L18.8036328,16.2435 Z M18.7425,15.6124537 L14.5775,15.6124537 L14.5775,12.7095463 L18.7425,12.7095463 L18.7425,15.6124537 Z M18.7425,12.0785 L14.5775,12.0785 L14.5775,8.12804634 L18.7425,8.12804634 L18.7425,12.0785 Z M13.7445,4.5815 L9.5795,4.5815 L9.5795,7.497 L13.7445,7.497 L13.7445,4.5815 Z M13.7445,8.12804634 L9.5795,8.12804634 L9.5795,12.0785 L13.7445,12.0785 L13.7445,8.12804634 Z M13.7445,12.7095463 L9.5795,12.7095463 L9.5795,15.6124537 L13.7445,15.6124537 L13.7445,12.7095463 Z M3.7485,4.5815 L4.5815,4.5815 L4.5815,16.2435 L3.7485,16.2435 L3.7485,4.5815 Z M0,3.7485 L3.7485,3.7485 L3.7485,4.165 L0,4.165 L0,3.7485 Z" id="Combined-Shape" fill="#AAAAAA"></path>
<rect id="Rectangle-324" fill="#FAC10C" x="0" y="3.7485" width="4.5815" height="12.495"></rect>
<rect id="Rectangle-324-Copy" fill="#FAC10C" transform="translate(11.733102, 2.290750) rotate(90.000000) translate(-11.733102, -2.290750) " x="9.44235203" y="-5.73533321" width="4.5815" height="16.0521664"></rect>
</g>
<g id="Filter" transform="translate(232.407000, 6.247500)">
<rect id="Rectangle-108" fill="#3BA3F8" x="4.73505679e-14" y="0" width="12.9115" height="1.2495"></rect>
<rect id="Rectangle-109" fill="#3BA3F8" x="4.73505679e-14" y="2.499" width="12.9115" height="1.2495"></rect>
<path d="M6.33160096,10.829 L6.45575,10.9678333 L6.57989904,10.829 L6.33160096,10.829 Z M4.8418125,9.163 L4.73505679e-14,3.7485 L12.9115,3.7485 L8.0696875,9.163 L4.8418125,9.163 Z" id="Combined-Shape" fill="#8EC9FB"></path>
<path d="M4.5815,9.163 L7.9135,9.163 L7.9135,18.326 L4.74601819,14.5516554 L4.5815,9.163 Z" id="Rectangle-111" fill="#8EC9FB"></path>
</g>
<g id="Sort" transform="translate(206.167500, 1.666000)">
<rect id="Rectangle-120" fill="#E2A364" x="10.829" y="4.165" width="2.0825" height="17.9095"></rect>
<path d="M15.5828281,14.5775 L17.1391718,15.7502891 L12.3853437,22.0588321 L10.829,20.886043 L15.5828281,14.5775 Z" id="Rectangle-121" fill="#E2A364"></path>
<text id="A" font-family="SFUIDisplay-Semibold, SF UI Display" font-size="11.902719" font-weight="500" letter-spacing="-0.107586779" fill="#19AF5C">
<tspan x="0.4165" y="11">A</tspan>
</text>
<text id="Z" font-family="SFUIDisplay-Semibold, SF UI Display" font-size="11.902719" font-weight="500" letter-spacing="-0.107586779" fill="#19AF5C">
<tspan x="0.4165" y="22.662">Z</tspan>
</text>
</g>
</svg>
<svg>
<use xlink:href="Pivot"></use>
</svg>
You want to know why the "Pivot" element is not showing up in your second SVG? Is that right?
The reason is because you are not referencing it correctly. You should have written:
<svg>
<use xlink:href="#Pivot"></use>
</svg>
Note the missing hash ("#") symbol.

How I can set auto height to svg element?

I have svg element and 4 rect in it.
<svg class="quarterly-graph">
<rect y="0" width="100%" height="5" transform="translate(0,0)" style="fill: #ffffff;"></rect>
<rect y="5" width="100%" height="90" transform="translate(0,0)" style="fill: #e7f0f5;"></rect>
<rect y="95" width="100%" height="5" transform="translate(0,0)" style="fill: #ffffff;"></rect>
<rect y="100" width="100%" height="5" transform="translate(0,0)" style="fill: #e7f0f5;"></rect>
</svg>
I have simple css for it:
.quarterly-graph {
height : auto;
border: 1px #000000 solid;
}
How I can set svg height equal height of 4 rects?
jsfiddle example: http://jsfiddle.net/CpZQY/
AFAIK you cannot do this with pure CSS. Your SVG does not specify all required attributes and thus default sizes will be used instead. You will have to specify the viewport/size at the SVG element. This should be no problem, because you have hard coded values in the rects, too. If you add a width="105px" to your svg element you do not need the CSS height directive anymore.

Categories