d3 append copy of g element on click to separate div - d3.js

I have in-line svg code three rect elements I've copied into my html document from Inkscape. I would like to be able to click any one of these rects and have all three of them appear in a separate div on the right side of the screen, zoomed in. I have enclosed each individual rect in a g element with an ID. I have also enclosed all three rects in a class tag of "section1".
Here's the fiddle: https://jsfiddle.net/hh2ek44m/
I have tried all sorts of combinations like this to append the clicked on g element or group of g elements to the #zoombox div.
d3.selectAll("g")
.on("click", function(d) {
d3.select("#zoombox").append("#section1")
});
I have also experimented with using 'this'.
In my working file (very large), I have many sections of rows that appear too small on the screen to visually inspect, and I would like the user to be able to click to zoom in on a section of rows in a separate space on the screen. I'd like to the original rows to remain unchanged, and for the zoomed in section image area to update when a new section is clicked.
I know I'm asking a lot here, so if you could push me in the right direction at all I'd appreciate it.
I've looked at many examples of on-click behavior such as http://bl.ocks.org/eesur/4e0a69d57d3bfc8a82c2 and http://bl.ocks.org/d3noob/5d621a60e2d1d02086bf. I eventually want my three rows to be on the left side of the page, and the zoomed in image of the three on the left, similar to this beautiful d3 vis, http://bl.ocks.org/syntagmatic/0613ee9324e989a6fb6b. Thanks.

This code has some problems:
d3.selectAll("g").on("click", function(d) {
d3.select("#zoombox").append("#section1")
});
First, since #zoombox is a <div>, you cannot append an SVG element directly to it. You have to append an SVG first:
d3.select("#zoombox").append("svg")
But, even doing that, the next step is not simple: you cannot append an ID to it. So, you could probably append an selection:
var myGroup = d3.select("#section1")
But this will not work as well, because the selection is an array. The logical solution would be using the SVG element itself:
var myGroup = d3.select("#section1").node()
But, again, this will not work!
Solution (out of many): Use the SVG use element:
var myGroup = d3.select(".section1");
var myGroupId = d3.select(myGroup).node().attr("id");//get the ID of the group
myGroup.on("click", function(){
d3.select("#zoombox")
.append("svg")
.append("use")
.attr("xlink:href", "#" + myGroupId);//the ID of the cloned group
});
Here is a demo (I changed your SVG a bit, it was too big for this snippet), click on your group to clone it:
var myGroup = d3.select(".section1");
var myGroupId = d3.select(myGroup).node().attr("id");
myGroup.on("click", function(){
d3.select("#zoombox").append("svg").append("use").attr("xlink:href", "#" + myGroupId);
});
div {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id ="zoombox"></div>
<svg width="100">
<g class = "section1" id="myUniqueID">
<g id="section114r1">
<rect
fill="0000ff"
id="section114r1"
width="3"
height="30"
x="35.0462"
y="58.15918" /></g>
<g id="section114r2"><rect
fill="#0000ff"
id="section114r2"
width="3"
height="30"
x="30.88818"
y="58.159" /></g>
<g id="section114r3">
<rect
fill="#0000ff"
id="section114r3"
width="3"
height="30"
x="26.73694"
y="58.15927" /></g>
<g id="section114r4"><rect
fill="#0000ff"
id="section114r4"
width="3"
height="30"
x="22.56696"
y="58.17471" /></g>
</g>
</svg>

Related

Is it possible to have a scrolling div that adjusts to the number of bars in a dc.js rowchart

I have several rowcharts that are connected to each other with dc.js.
These rowcharts have the x axis at the top in a different div, as explained here. However these rowcharts also implement filtering and removing, therefore, whenever I filter in one rowchart, the number of bars in the others reduce, but it maintains the size of the scrollable div, even though there are no bars below what is shown. Also, I'm pretty sure it is easy, but I haven't figured out how to put the reset button below the chart, because it shows between the chart div and the axis div, as seen below.
Is there a way to correct these issues?
This is what I have in each rowchart div:
<div id='axis'></div>
<div id="chart" style="overflow-y:auto; height:200px;">
<div>
<span class="reset" style="display: none;">Phylum seleccionado(s):
<span class="filter"></span>
<a class="reset" href="javascript:Chart.filterAll();dc.redrawAll();" style="display">Reset</a>
</span>
</div>
</div>
And this is what a I have in each rowchart in the main.js:
Chart
.fixedBarHeight(20)
.height(nonEmpty.all().length * 20 + 1)
.margins({top: 0, right: 20, bottom: 0, left: 20})
.width(600)
.xAxis(d3.axisTop())
.elasticX(true)
.ordinalColors(['#e41a1c'])
.gap(1)
.dimension(Dim)
.group(nonEmpty) //this removes the ones that don't match the filter of the other rowchart
.on('pretransition', function () {
Chart.select('g.axis').attr('transform', 'translate(0,0)');
Chart.selectAll('line.grid-line').attr('y2', Chart.effectiveHeight());
});
Partial answer here. Well, it answers what you asked but doesn't answer the next question I expect. :(
You are currently sizing the chart based on the number of bars, and you will need to resize the chart when the number of bars change.
This should be done in a preRedraw handler, but unfortunately preRedraw is currently fired after resizing is done. So currently you have to override .redraw():
dc.override(chart, 'redraw', function() {
chart.height(chart.group().all().length * 21);
return chart._redraw();
})
As for putting the info & controls after the chart, there are two issues and I was only able to solve one of them.
dc.js will append an svg element to the chart div. It'd be nice to have more control over this but I'm not sure what to do. For the moment, the easiest workaround is to re-append the info div:
chart.on('postRender', function() {
chart.root().node().appendChild(chart.select('div.info').node())
})
However, what you really want is probably to pull the info div out of the scrolling div, and I can't figure that out at the moment. The problem is that the chart expects the controls/info to be inside the chart div, and that's the same one that needs to scroll.
I tried to mess around with adding another scrolling div inside the chart div, and then moving the svg under it. This works, but it creates some annoying flashing.
Here's what I got so far. I may return to this later if I think of a better solution.
Update: controls outside of the scroller
To solve this right, we need the controls outside of the chart div.
We can port baseMixin.turnOnControls to a filtered event handler, and do the same showing and populating that the chart would do:
function turnOnControls(_chart, controls) {
var attribute = _chart.controlsUseVisibility() ? 'visibility' : 'display';
controls.selectAll('.reset').style(attribute, null);
controls.selectAll('.filter').text(dc.printers.filters(_chart.filters())).style(attribute, null);
}
function turnOffControls(_chart, controls) {
var attribute = _chart.controlsUseVisibility() ? 'visibility' : 'display';
var value = _chart.controlsUseVisibility() ? 'hidden' : 'none';
controls.selectAll('.reset').style(attribute, value);
controls.selectAll('.filter').style(attribute, value).text(_chart.filter());
}
function filter_function(controls) {
return chart => {
chart.hasFilter() ?
turnOnControls(chart, controls) :
turnOffControls(chart, controls);
}
}
chart.on('filtered', filter_function(d3.select('#info')));
Also, controlsUseVisibility and visibility: hidden is better when the controls will affect layout when shown/hidden.
(fiddle)

Media query in d3.js only partially successful

I want to resize a d3.js chart using media queries, and apply transitions. The graph setup in html is:
<g id="d3js_graph">
<svg class="d3svg"></svg>
<script src="JS\D3_BarChart.js"></script>
</g>
I applied a media query to the d3svg class and used a transform:
#media (min-width: 1080px) {
.d3svg {
transform: translate(-30px,-30px); }
}
That works to move the graph, but now the graph shows with the left half blanked out, which implies that the graph but not its container have been moved. So I reversed it and applied the media query to the d3js_graph ID (in the opening g tag above):
#media (min-width: 1080px) {
#d3js_graph {
transform: translate(-30px,-30px); }
}
But that does nothing, so that’s not the element. It’s the .d3svg class shown above. I could use a transition in the d3.js file:
d3.select("body").transition().style("color", "red");
but how to I do d3.select within a media query in the d3.js file? I added a media query in the file, but it simply caused the graph to disappear from the screen.
So my question is: why is the graph partially blanked out on the left side when I move it using a transform? What’s the controlling element for a media query on d3.js?
Thanks for any help.
This...
<g id="d3js_graph">
<svg class="d3svg"></svg>
<script src="JS\D3_BarChart.js"></script>
</g>
... is invalid. <g> elements are SVG elements, and can only be used inside SVGs.
What you want is an HTML <div>:
<div id="d3js_graph">
<svg class="d3svg"></svg>
<script src="JS\D3_BarChart.js"></script>
</div>
Also, I'd consider moving that <script> to outside the div.

compare class of path elements to a dataset

I am really new to d3 and I want to make a choropleth map with the d3 datamaps so if have to compare the classes of the pathways to my dataset. So far I did manage to get the path's
html
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"></script>
<script src="/d3map/datamaps.world.min.js"></script>
<div id="container" style="position: relative; width: 500px; height: 300px;"></div>
<script src="d3map.js"></script>
js code
var map = new Datamap({element: document.getElementById('container'),
})
var country = d3.selectAll("path")
Country is an array containing 177 elements
one element in country has this form:
<path d="bunch of numbers" class="datamaps-subunit AFG" style="etc"></path>
i want to see if the country code in the class (e.g. AFG) matches one in my dataset.
Country.class gives undefined
I am probably missing something really obvious
Here's an example fiddle: https://jsfiddle.net/g2v5sgmz/
You can filter specific classed elements in an array with the filter method like this:
var country = d3.selectAll("path");
var afg = country.filter(function(d,i){
if( d3.select(this).classed("AFG") ){
return d3.select(this);
}
})

On hover effect on a SVG group

I'm working on a pretty big map with lots of different areas and text on top of them, sort of like countries. I want to add a on mouse hover effect when hovering over this area. The effect should add a shadow and change its opacity. When I hover out of the area the opacity should go back to its original and the shadow effect should go away. I managed to do this but when I add text into this area it messes things up. The on hover effect gets called again when I hover on the text that's inside area when it shouldn't be called again at all. I made a fiddle that works until i hover on the text as well. I'm using the D3.js library for the coding.
var groupAreas = "";
groupAreas = d3.selectAll(".area-group");
groupAreas.on("mouseover", fadeInArea)
.on("mouseout", fadeOutArea)
.on("click", zoomIn);
var initialOpacity = 0;
function fadeInArea() {
//Get its initial opacity
initialOpacity = d3.select(this).select("path").style("opacity");
d3.select(this).transition(500).attr("filter", "url(#shadow-filter-1)").style("cursor", "pointer");
d3.select(this).select("path").transition(500).style("opacity", 0.7);
}
function fadeOutArea() {
d3.select(this).transition(500).attr("filter", null).style("cursor", "default");
d3.select(this).select("path").transition(500).style("opacity", initialOpacity);
}
https://jsfiddle.net/qx5u9uo4/6/
the area has an original opacity of 0.4. When i hover over the area it changes to 0.7 and the shadow effect is added. When I go out, it goes back to 0.4 as it should. Yet if i go in the area and also hover over the text the OnMouseOver gets called again and it then sets 0.7 as original opacity and thus never goes back to 0.4 again when I actually go outside the area.
I think the problem may lie with my order of DOM svg elements?
<g class="area-group">
<path id="area-1" d="M502.2 581.4h-53.5v-61l167.4 70.4v34.6H502.2z" />
<text transform="translate(542.4 614)"> FOO </text>
<text transform="translate(459.4 571.2)"> BAR </text>
<text transform="translate(521.1 592.9)"> / </text>
</g>
Sounds like you just want to stop the text elements from receiving pointer events:
.area-group>text {
pointer-events: none;
}

Highlight multiple path elements within SVG <g> on event action

I have an SVG which has multiple path elements contained within a group (it's a map with multiple islands). I want all the islands (each represented by a path within the group) to be highlighted when the user initiates an action such as onclick.
I can get each element to highlight by doing this:
<script type="text/ecmascript">
<![CDATA[
var _undo_render_hash = new Object;
function onAreaClick(evt) {
// Undo previous highlight ...
for(var i in _undo_render_hash) {
document.getElementById(i).setAttribute("style", _undo_render_hash[i]);
}
// Reset undo stack ...
_undo_render_hash = new Object;
// Highlight this ...
var change = evt.target;
_undo_render_hash[change.id] = change.getAttribute("style")
change.setAttribute("style", "opacity:1; stroke:green; stroke-width:3");
}
]]>
</script>
And then in the document having something like this:
<g id="Comhairle_nan_Eilean_Siar" onclick="onAreaClick(evt)">
<path d="M218.641,49.68c-7.79,6.53-14.651,13.989-19.681,23.28c5.158-0.089,5.851,5.187,10.561,5.52
c7.229,0.514,8.971-7.178,15.359-8.88c0.016,1.914-0.065,5.869-0.96,7.92c-3.403,7.813-18.518,2.504-25.199,1.2
c-2.494,4.745,2.057,11.877-2.16,14.4c-3.206,1.918-8.535-0.242-10.08,3.6c-0.606,1.507-0.103,3.258,0,4.8
c1.748,1.852,4.421,0.283,6.72,0c1.417,2.63,0.715,6.368-2.88,6c0.886,2.395,3.484,3.076,3.6,6.24
c-4.017,1.442-10.129-0.689-14.159,0.72c0.559,1.287,3.194,1.784,3.6,3.601c0.75,3.359-3.252,6.585-3.12,10.08
c-4.764-1.083-9.61,3.419-14.16,2.88c-2.724-0.322-3.863-2.384-5.76-4.08c-0.331,1.269-0.527,2.673-1.44,3.359
c-2.143,0.743-4.523-0.247-5.76-1.199c-1.449,1.431-3.391,2.368-4.8,3.84c1.44,3.359,4.801,4.798,5.76,8.64
c-3.942-0.331-10.234-3.217-14.88-2.88c-6.333,0.46-3.063,7.522-0.479,10.32c-2.172,2.07-5.325,0.009-7.681,0.239
c-4.061,0.398-8.587,6.609-11.279,9.36c-3.73,3.812-6.437,7.813-10.561,9.84c-1.912-4.06-3.364-6.849-6.479-9.6
c-3.927-3.466-10.139-4.854-10.08-11.04c3.815,0.022,4.96,2.719,6.239,5.28c6.865-1.135,5.284-10.717,14.16-9.841
c0.157-1.276-1.402-3.928-0.479-6c4.959-2.283,13.127,3.235,17.52-0.72c-0.652-0.853-0.797-2.692-1.2-4.32
c-0.391-1.582-1.24-3.078-0.96-3.84c-6.283-5.527-17.934-6.946-26.159-11.04c-2.425-1.206-6.217-2.982-5.04-6.72
c4.851,0.01,9.509-0.931,9.119-5.76c-0.326-4.061-5.008-4.486-7.68-6.72c-0.909-2.646-1.201-5.376-1.68-8.16
c-0.46-2.676-1.58-5.595-1.2-8.16c0.578-3.899,5.171-6.548,3.36-10.8c1.446-0.008,2.471,0.408,4.08,0.239
c0.734-4.944,1.731-9.628,4.56-12.479c7.463,4.137,13.87,9.33,17.76,17.04c2.904-1.336,4.214-4.267,5.76-6.96
c3.43,1.182,9.073,2.812,9.841-0.96c1.174-5.771-7.64-13.473-3.841-19.2c1.145-1.725,4.916-3.022,7.44-4.32
c2.276-1.17,5.278-3.83,7.68-3.84c0.969-0.004,2.588,1.384,3.841,1.44c3.428,0.153,8.889-3.59,11.76-6.24
c6.317-5.831,14.279-9.8,22.319-13.68c4.311-2.081,8.819-3.566,12-6c2.371-1.814,3.864-4.521,5.521-6
c0.204-0.044,0.227,0.094,0.24,0.239c2.891,1.724,5.448,4.414,8.16,7.681c2.588,3.118,5.115,5.628,5.279,9.359
C221.368,34.817,211.062,43.433,218.641,49.68z"/>
<path d="M44.16,169.2c1.14,1.858-0.598,3.167-0.24,5.52c0.411,2.7,3.443,4.906,6.24,5.04
c8.161,0.392,14.983-9.597,23.04-9.12c1.205,1.26,2.406,2.029,2.4,3.36c-0.01,1.919-2.489,2.896-2.4,4.56
c0.055,1.041,1.147,2.174,1.92,2.641c4.312,2.598,13.878-0.731,17.28,2.88c0.164,4.52-5.563,9.653-5.76,2.159
c-1.912,0.602-2.41,1.726-4.32,1.681c-4.39-0.104-6.364-7.368-10.561-7.44c-1.626-0.027-3.817,0.905-4.079,3.12
c-0.598,5.049,5.754,6.326,7.68,9.601c-2.523,7.254,7.267,4.608,11.76,4.8c0.29,4.37-3.093,5.066-5.52,6.72
c-4.387-5.06-17.802-5.835-23.04-0.96c5.834,3.937,12.206,8.58,22.08,7.68c0.258,1.184,1.034,1.846,1.199,3.12
c-2.392,2.849-7.622,1.498-11.76,1.68c-2.038,0.091-4.323,0.584-6.24,0.24c-2.577-0.462-4.418-2.99-6.479-3.359
c-4.983-0.896-7.753,2.42-12.24,1.92c-3.857-2.997-3.507-8.639-5.76-12.721c-3.031-5.492-10.032-7.792-18.24-6.479
c-2.548-2.172-4.34-5.101-7.2-6.96c4.087-2.713,2.958-10.643,5.28-15.12c5.915-2.263,6.876,9.052,13.68,7.92
c4.908-0.816,6.09-10.913,11.04-12.479C44,169.2,44.08,169.2,44.16,169.2z"/>
<path d="M60.48,224.88c-1.123,3.356-5.712,3.248-7.44,6c-0.189,4.43,5.952,2.528,8.16,4.56
c-5.562,1.186-12.328-0.868-18-1.439c-0.145-6.089-5.911-1.854-9.12-2.641c-2.348-0.574-4.384-6.269-2.64-8.88
c2.262-3.386,14.088-4.188,18-1.2c1.309,1,0.646,2.567,2.64,3.601C54.909,226.023,58.178,223.486,60.48,224.88z"/>
<path d="M38.88,238.561c1.151,5.955,11.284,5.267,9.36,12.72c0.696,1.465,2.814,1.506,4.56,1.921
c-0.191,1.327-2.382,0.657-2.64,1.92c0.414,4.786,3.711,6.688,3.84,11.76c-1.044,1.436-2.94,2.019-5.279,2.16
c-0.516,5.726-1.513,10.968-7.2,11.52c0.217,3.38,3.585,5.16,4.08,8.88c0.644,4.846-0.931,7.977-4.32,9.84
c-0.208,4.385,8.039,3.485,7.44,7.44c-0.272,1.796-5.073,2.562-7.921,2.64c-5.482,0.151-10.604-1.434-12.479-4.8
c-4.678-8.392-3.109-23.416-3.12-35.279c6.781-5.746-2.917-23.356,2.64-29.761C29.915,237.129,35.103,237.705,38.88,238.561z"/>
<path d="M15.84,322.801c1.478,2.442,2.316,5.523,3.601,8.16c-4.067,0.868-7.218,5.468-11.76,5.279
c-2.323-0.096-2.966-1.522-5.521-1.68C5.635,329.556,9.217,324.657,15.84,322.801z"/>
</g>
However, only each one of the islands lights up. I'm just getting to grips with SVG - please can someone advise how to structure the document so that each element within the group will have the action applied to it?
Here is a simple example showing how you can use CSS classes on the parent group to easily style the individual elements:
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
<meta http-equiv="content-type"
content="application/xhtml+xml; charset=utf-8" />
<title>SVG Styling via CSS Classes</title>
<style type="text/css" media="screen">
body { background:#eee; margin:2em }
svg { display:block; border:1px solid #ccc; background:#fff; margin:auto }
.islands * { stroke-width:2px; stroke:#700; fill:#620; fill-opacity:0.5 }
.over * { stroke-width:4px; stroke:#930; fill:#ff0; fill-opacity:0.9 }
</style>
</head><body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full"
viewBox="-350 -250 700 500" width="800px" height="600px">
<g class="islands"><circle r="70" /><circle cx="150" cy="20" r="50" /></g>
</svg>
<script type="text/javascript"><![CDATA[
var svg = document.getElementsByTagName('svg')[0];
var svgNS = svg.getAttribute('xmlns');
svg.addEventListener('mouseover',function(e){
var g = e.target.parentNode;
g.setAttribute('class',g.getAttribute('class')+' over');
},false);
svg.addEventListener('mouseout',function(e){
var g = e.target.parentNode;
g.setAttribute('class',g.getAttribute('class').replace(' over',''));
},false);
]]></script>
</body></html>
You can see this as a working example on my site:
http://phrogz.net/SVG/css-driven-styles.xhtml
To answer your original question, you would do something like the following:
function onAreaClick(evt){
var island = evt.target;
var parent = island.parentNode;
var others = parent.getElementsByTagName('path');
for (var i=0,len=others.length;i<len;++i){
others[i].setAttribute(...);
}
Variations might include using parent.childNodes, or using direct presentational attributes (e.g. others[i].stroke='#0f0';) instead of setting the style attribute as a string.
This may not directly answer your question, but it is related, and hopefully of some use.
You can use CSS :hover rules to do mouseover highlighting without even using scripting.
<style>
*:hover { fill: red !important; }
</style>
Here's an example showing that, the style is at the top of the file, and there's a short script snippet at the end to reuse the same map data for the zoomed-in view.
Create a polyline for your path, using as many as points you want.
<polyline points="fill points of your paths here" style="">
<set attributeName="stroke" from="original colur" to="red" begin="mouseover" end="mouseout"/>##
</polyline>
Note: attributename="stroke" is important!

Resources