Media query in d3.js only partially successful - d3.js

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.

Related

d3 append copy of g element on click to separate div

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>

I can't make a right legend by d3.js

This is my result belowing:
But what I want is put every bars together like this:
And follow the belowing is my major code about legend:
The most traditional (and versatile) way for creating such legends among D3 community is using <text> and <rect> SVG elements (which you can position the way you want). But once you're using HTML <li>, try one of these two approaches in your CSS:
li {
display: inline;
}
Or
li {
float: left;
}

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;
}

How to draw only the visible pixels which are >0% alpha with a custom color in canvas?

I would like to make a good performance hit test for png images and other shapes. I don't really care what shapes they are because with this technique there is no performance issues at checking (not setup).
I intent to collect all the images on the screen in a secondary canvas just for hit test. For each image drawn I will create a new color which is attached to that particular image. Then I draw all of them in the canvas, each image will have a different fill color.
When I click on a pixel (x, y) it will get the color (r, g, b). Every color is mapped to a image, so I get the image clicked with no error (I don't waste with finding what was hit with that click).
I know it will be limited to 256*256*256=16 777 216 items because those are all the colors but I don't think it will be a problem for now...
So what I really need is to know how to put those fill colors on the secondary canvas which is based only on the visible pixels for each image.
UPDATE
As you can see to the right it's the hit test map. So if I click on the black shade (c) I instantly know I've clicked on the blue box without any other calculation.
One improvement it would be to cache the alpha data. Also reuse the same alpha data for each image instance (we must take care about scaling and rotation...).
thanks
Here’s how you would color-mask the non-transparent pixels of a canvas image.
Be sure you replace "PutYourImageHere.png" with your own image url.
<!doctype html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid blue;}
</style>
<script>
$(function(){
var img=new Image();
img.onload=function(){
var red=255;
var blue=0;
var green=0;
var canvasCopy=document.getElementById("canvasCopy");
var ctxCopy=canvasCopy.getContext("2d");
var c=document.getElementById("canvas");
var ctx=c.getContext("2d");
ctx.drawImage(this,0,0);
var imgData=ctx.getImageData(0,0,c.width,c.height);
for (var i=0;i<imgData.data.length;i+=4)
{
if(imgData.data[i+3]>0){
imgData.data[i]=red;
imgData.data[i+1]=green;
imgData.data[i+2]=blue;
imgData.data[i+3]=255;
}
}
ctxCopy.putImageData(imgData,0,0);
}
img.src = "PutYourImageHere.png";
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
<canvas id="canvasCopy" width="300" height="300"></canvas>
</body>
</html>

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