What I'm trying to do is relatively simple but I'm new to JS and D3.js.
I have created a bunch of rectangles using SVG through D3.js.
I added some code to handle a click event and in there I'd like to iterate through all drawn nodes and do something with them as long as a specific property matches the same property in the one that's been clicked.
Here's the code that draws the rectangles (only one of them here);
d3.select("svg")
.append("rect").attr("x", 50)
.attr("y", 10)
.attr("height", 20)
.attr("width", 200)
.attr("title", "catalog")
.style("fill", "#CB4B19")
.on("click", mouseClick)
And here's how I'm trying to retrieve the "title" property of each rectangle drawn and compare it to the clicked one (and in this case, just log it in the console). I know this is probably basic but I can't figure out what I'm doing wrong here.
function mouseClick(d) {
var t = d3.select(this).attr("title"); //store the "title" property of the clicked rectangle
d3.selectAll("rect").each(function(d, i){ //Select all rectangles drawn
if(d3.select(this).attr("title") == t){ //for each one, if the "title" property = the one initially chosen
console.log(t); //do something here
}
})
}
Your code actually seems to be working correctly. At least for me it did. One thing I will say is that d3 does mimic jQuery syntax in that it lets you select elements with attributes with the d3.select('element[attributeName="?"]') syntax. You can read more about selections here.
So for your example, you could do
var t = d3.select(this).attr("title");
// select all rectangles with the attribute title
d3.selectAll("rect[title='" + t + "']").each(function(d, i){
console.log(t);
});
You no longer need the if statement to check because you are only selecting them. I made a simple jsFiddle to show this. I made 3 different types of rectangles with different title attributes and when you click on them, it only selects rect that have the same title attribute.
http://jsfiddle.net/augburto/znqe8nqr/
Related
This is probably a really basic question, but I can't figure it out. I'm building a map using D3. My code creates and svg, appends a g element to it and then draws the map within it. What I want is to render a set of buttons and controls that are positioned inside the map viewer. They would be zoom buttons, a dropdown to display different sets of data and a timeline slider.
For example, with the dropdown selector I want placed I did this:
I tried using d3 as in:
svg.append("select")
.attr("class", "field_dropdown")
.data(['housing_unit', 'tenure', 'median_contract_rent', 'median_value', 'median_income'])
.enter()
.append("option")
.attr("value", function(d) {
return d
});
but this rendered the select item and option items separate from each other, and not even visible within the map container.
As mentioned, not only do I wanna add a dropdown, but also buttons for zoom and a slider, among other items. How do I render and position them in the map container?
Thanks
This has been asked several times (surely a duplicate): you cannot append HTML elements ("div", "p", "select", "h1" etc) to an SVG. It will simply not work.
The best solution, in your case, is creating the drop down menu and the other controls outside the SVG, in the HTML.
But, if you really want to create this drop down inside the SVG (which I don't advise), you can use foreignObject (which will not work on IE):
var foreign = svg.append("foreignObject")
.attr("width", 100)
.attr("height", 100)
.append("xhtml:body");
var select = foreign.append('select')
.attr("class", "field_dropdown")
//the rest of your code
I'm looking for some advice on how to get two elements in a visualization, which are linked by a common data value, to respond simultaneously.
Here is the visualization as it stands now.
http://bl.ocks.org/natemiller/2686e5c0d9a1a4bb0895
Note that the different colored points are for the 50 US states in 2005 (green) and 2013 (blue), so there is a blue point and a green point for each state. I have two things I would like to get working here.
I would like to be able to mouseover either a blue point or a green point and have the corresponding point (for the same state) highlighted.
I would like a tooltip with some basic data to appear next to both points, providing point specific data.
Regarding the first point above. Right now when you mouseover a blue point the corresponding green point is highlighted, however, when you mouseover a green point only that point is highlighted and not its corresponding blue point. I imagine this is a simple fix, but for the life of me I can't figure out to reverse the reference so I get green to blue references as well.
Regarding the second point. Right now a tooltip with relevant information appears near the moused-over point, but I would like to have a similar tooltip appear next to the corresponding point from the alternate year of data, so that direct comparisons across years are easier. I am quite new to adding HTML tooltips so I'm not clear on how to do this and suspect it may require a new method for adding tooltips. Can any help to steer me in the correct direction for how to have a tooltip appear near the moused-over element and a corresponding linked element?
1) Remember that ids are unique and you're creating multiple circles with the same id, use a class instead
circles.attr("class", function(d) { return d.state })
2) You're creating a single tooltip, if you want to show one for each pair of states create multiple tooltips
Assuming that you make these changes you can easily create multiple tooltips for each pair of states
circles.on('mouseover', function (d) {
// selection for 2 states
var states = d3.selectAll('circle.' + d.state)
// code to style those nodes goes here ...
// tooltips for the states
var tooltips = d3.select('svg').selectAll('text.tooltip')
.data(states.data())
// initial styling of the tooltips goes here...
tooltips
.enter()
.append('text')
.attr('class', 'tooltip')
// update
tooltips
.html(function (d) {
// text of the tooltip
return 'something'
})
// positioning, it requires some margin fixes I guess
.attr('x', function (d) { return xScale(d.child_pov) })
.attr('y', function (d) { return yScale(d.non_math_prof) })
})
Finally remove the tooltips created on mouseover when the mouseout event is triggered
circles.on('mouseout', function (d) {
d3.select('svg').selectAll('text.tooltip').remove()
})
You cannot have multiple elements with the same id. Use a class .circleHawaii instead of an id #circleHawaii.
I have read several articles and tutorials but couldn't find a sufficient answer on how to select the parent div of the svg using d3.select. I basically just want to append a tooltip to the div which contains my chart like this.
//this selection probably doesn't make sense...
var tooltip = d3.select("#pie-svg").select(this.parentNode).append("div")
.attr("class", "piechart-tooltip")
.style("opacity", 0);
Along the lines of this question:
var tooltip;
d3.select("#pie-svg").each(function() {
tooltip = d3.select(this.parentNode).append("div")
.attr("class", "piechart-tooltip")
.style("opacity", 0);
});
The problem with the code you've posted in the question is that this won't be defined (or set to the right element) in this context. When used with .each(), it will.
I realise this question has been asked before but I can't get to the bottom of it.
Here is my chart... http://www.gogeye.com/financialnews/piechart/index3.html
All I want to do is have the coin render behind the graph. I know D3 renders in order they are appended.
I have tried to re-append the coin but can't seem to get it working.
I've tried reordering when things are appended in the DOM but keep getting errors probably because variables are getting called before being defined etc.
Can someone give me an example of how to fix this with my code? I don't want you to do the work for me but I've been pulling my hair out for so long, I can't seem to apply other peoples examples to mine.
thanks
I would recommend creating some "layers" using svg g elements which stands for "group".
When you render your chart, you can first define your layers:
var layer1 = svg.append('g');
var layer2 = svg.append('g');
var layer3 = svg.append('g');
// etc... for however many layers you need
Then when you append new elements, you can decide which layer you want them to be on, and it won't matter what order you assign them in, because the group elements have already been added to the DOM, and are ordered. For example:
var layer1 = svg.append('g');
var layer2 = svg.append('g');
var redCircle = layer2.append('circle')
.attr('cx', 50)
.attr('cy', 50)
.attr('r', 16)
.attr('fill', 'red')
var blueSquare = layer1.append('rect')
.attr('x', 25)
.attr('y', 25)
.attr('width', 50)
.attr('height', 50)
.attr('fill', 'blue');
In this case the red circle will be visible above the blue square even though the blue square was created last. This is because the circle and the square are children of different group elements, which are in a pre-defined order.
Here's a FIDDLE of the above example so you can see it in action.
Doing this should take a lot of the guesswork out of when to add certain elements to your chart, and it also helps to organize your elements into a more logical arrangement. Hope that helps, and good luck.
I am using the D3.js, and found that it has a built-in function for changing the z-order of SVG elements programmatically after the original drawing.
RipTutorial: svg--the-drawing-order covers the d3 builtin function
Quotes from this link:
selection.raise(): Re-inserts each selected element, in order, as the last child of its parent. selection.lower(): Re-inserts each selected element, in order, as the first child of its parent.
d3.selectAll("circle").on("mouseenter", function(){
d3.select(this).raise();
});
d3.selectAll("circle").on("mouseleave", function(){
d3.select(this).lower();
});
see live example their jsFiddle
I'm working on fading out certain lines from a chord diagram when a separate chart is hovered over. I'm looking at the code for the "fade" function,
`function fade(opacity) {
return function(g, i) {
svg.selectAll(".chord path")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("opacity", opacity);
};
}`
And I was just wondering if someone could explain to me what d.source.index means and d.target.index mean. I generally understand that it's the source of the chord and the target of the chord, but I wanted to gain some more specific insight into the values/meaning of "index" so that I could better manipulate the selection.
My ultimate goal is to over over a box in a separate legend rectangle, and have the chord diagram fade so that only the color hovered over in the legend box retains full opacity.
Every chord has data associated with it. This data has, among other things, source and target attributes, which point to the nodes that the chord connects. In the code above, the index attribute of the source and target nodes is referenced to identify which ones to filter. This can be anything you want though.
In your application, depending on what the legend is for, you would need to check the value displayed in the legend for the source/target nodes or the chord itself.