I'm coming to you today with another D3.js problem.
I have a simple bar chart.
When the mouse hovers a bar, a string is displayed on it.
This morning, I added a new transition that changes the background colour of the bar.
Now, the problem is the following:
If the .transition changing the background-color is placed under the one that displays the string, only the background colour changes, the string does not appear.
And if the .transition displaying the string is placed under the one that changes the background colour, only the string appears, without a change in the colour.
Here's a JSFiddle: http://jsfiddle.net/QbGRE/1/
d3.selectAll("div.bar")
.on("mouseover", function(d) {
d3.select(this)
.transition().duration(300)
.style("background-color", "#EE3B3B");
})
.on("mouseout", function(d) {
d3.select(this)
.transition().duration(300)
.style("background-color", "DarkRed");
});
d3.selectAll("div.line")
.append("div")
.attr("class","bar")
.style("width", function(d){return d.occurrence /10 + "px";})
.on("mouseover", function(d) {
d3.select(this)
.append("text").style("pointer-events", "none")
.text(function(d){return d.occurrence + " occurences";});
})
.on("mouseout", function(d) {
d3.select(this)
.select("text").remove();
});
Thank you all for your help, d3-savvy persons
The reason for this is that you're attaching mouse event handlers twice and the later ones overwrite the earlier ones. So first you're attaching the one that adds the text and then later you're attaching the one that changes the color which replaces the first.
The easiest way to fix this is to do everything you want to do on mouse events in one place:
.on("mouseover", function(d) {
d3.select(this)
.append("text").style("pointer-events", "none")
.text(function(d){return d.occurrence + " occurences";});
d3.select(this)
.transition().duration(300)
.style("background-color", "#EE3B3B");
})
.on("mouseout", function(d) {
d3.select(this)
.select("text").remove();
d3.select(this)
.transition().duration(300)
.style("background-color", "DarkRed");
});
Complete example here. Alternatively, you can use different namespaces for the event handlers:
.on("mouseover.text", function(d) {
// etc
});
.on("mouseover.color", function(d) {
// etc
});
Complete example here.
Related
Im trying to add a 'highlight' effect that is shown in this link(https://www.d3-graph-gallery.com/graph/parallel_custom.html) My lines are made up of two things, first is the line itself, and the second are dots that appear on each line. When my mouse hovers on the red line, the line stays the same but the red dots disapear for some reason. I want the red dots along with the red line to stay the same when i hover over it. What am i doing wrong here? the picture below shows what happens when i hover over the red line, and as you can see the red dots disappear
// Highlight individual line and dots
var highlight = function(d) {
var selected_line = d.key
// first every group turns grey
svgObj.selectAll(".line")
.transition().duration(200)
.style("stroke", "lightgrey")
.style("opacity", "0.2")
svgObj.selectAll(".dot")
.transition().duration(200)
.style("stroke", "lightgrey")
.style("opacity", "0.2")
// Second the hovered line takes its color
svgObj.selectAll("." + selected_line)
.transition().duration(200)
.style("stroke", color(selected_line))
.style("opacity", "1")
svgObj.selectAll("." + selected_line)
.transition().duration(200)
.style("stroke", color(selected_line))
.style("opacity", "1")
}
// UnHighlight
var doNotHighlight = function(d) {
svgObj.selectAll(".line")
.transition().duration(200).delay(50)
.style("stroke", function(d){ return( color(d.key))})
.style("opacity", "1")
}
// Draw the line
svgObj.selectAll(".line")
.data(sumstat)
.enter()
.append("path")
.attr("class", function (d) { return "line " + d.key} ) // 2 class for each line: 'line' and the group name
.attr("fill", "none")
.attr("stroke", function(d){ return color(d.key) })
.attr("stroke-width", 4.5)
.attr("d", function(d){
return d3.line()
.curve(d3.curveMonotoneX)
.x(function(d) { return x(d.year); })
.y(function(d) { return y(+d.n); })
(d.values)
})
//.on("mouseover", function(){return tooltip.style("visibility", "visible");})
//.on("mousemove", function(){return tooltip.style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
//.on("mouseout", function(){return tooltip.style("visibility", "hidden");})
.on("mouseover", highlight)
.on("mouseleave", doNotHighlight )
// Draw dots on points
svgObj.selectAll(".dot")
.data(data)
.enter()
.append("circle")
.attr("class", function (d) { return "dot " + d.key } ) // 2 class for each line: 'line' and the group name
.style("fill","white")
.style("stroke-width", "3px")
.style("stroke", function (d) { return color(d.name) })
.attr("cx", function(d) {return x(d.year); })
.attr("cy", function(d) {return y(d.n); })
.attr("r", 5.5)
It is because the data of .line and .dot is different, one is sumstat with key property, the other one is data without key. so the code
.append("circle")
.attr("class", function (d) { return "dot " + d.key } )
will return "dot undefined" for each circle's class.
And then in highlight() the selected dots will not be included.
I adjusted your code into a demo, but you might change the bind data of .line and .dot consistently, to avoid such mistake after.
I have a scatterplot based on this example. I would like highlight all the dots of one color when mousing over any single dot. Aka, if I hover over a green dot, all the green dots switch to full opacity.
I made a fiddle here: https://jsfiddle.net/nancynancy/mc3tz3dj/55/
As I understand it, my code currently only selects one dot, so I presume I need to create a group for each category of the species variable-- but I'm not sure what that looks like. In R the species variable would be a factor with different levels; what's the analog in D3?
// Part of the plot function
chart.selectAll(".dot")
.data(params.data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", function(d){ return responseScale(d.petalWidth);})
.attr("cx", function(d) { return x(d.sepalWidth); })
.attr("cy", function(d) { return y(d.sepalLength); })
.style("fill", function(d) { return color(d.species);});
// Create a group here, say ".species" that will replace ".dot"?
this.selectAll(".dot")
.on("mouseover", function(d){
d3.select(this)
.transition()
.style("opacity", 1)
})
.on("mouseout", function(d){
d3.select(this)
.transition()
.style("opacity", 0.1)
});
I would probably 'selectAll' the dots and then filter the selection to include only those which have a species property matching that of the mouseover-ed dot like this
chart.selectAll(".dot")
.on("mouseover", function(d){
d3.selectAll('.dot')
.filter(function(dot){
return (dot.species == d.species)
})
.transition()
.style("opacity", 1)
})
.on("mouseout", function(d){
d3.selectAll('.dot')
.filter(function(dot){
return (dot.species == d.species)
})
.transition()
.style("opacity", 0.1)
});
Note, also, I tend to avoid using this where possible as it's value can change depending upon the call site of the containing function -- this can make refactoring awkward
JS Fiddle
Small problem on one viz.
I have a bar chart, and I want it to display some text in the line on 'mouseenter'. That works fine, but I want to remove this text on 'mouseout', and I can't seem to get the hang of it.
Thank you very much for your help!
Attached: the concerned section of the d3.js script
d3.selectAll("div.line")
.append("div")
.attr("class","bar")
.style("width", function(d){return d.occurrence /10 + "px"})
.on("mouseenter", function(d) {
d3.select(this)
.append("text")
.text(function(d){return d.occurrence + " occurences"});
})
.on("mouseout", function(d) {
d3.select(this)
.select(".text").remove();
});
In your code, you're trying to select .text on mouseout. You're selecting nodes with which have the class: "text" rather than text nodes. Remove the dot.
I'd probably also change the mouseout "select" to "selectall" just in case you miss a mouseout event and accidentally add two text nodes.
Edit:
http://jsfiddle.net/QbGRE/
d3.select("div.line").selectAll("div.bar")
.data(data, function(d) { return d.id; })
.enter()
.append("div").attr("class","bar")
.style("width", function(d){return d.occurrence /10 + "px";})
.on("mouseover", function(d) {
d3.select(this)
.append("text").style("pointer-events", "none")
.text(function(d){return d.occurrence + " occurences";});
})
.on("mouseout", function(d) {
d3.select(this)
.select("text").remove();
});
Cleaned up your code.
"mouseover/mouseout" instead of "mouseenter/mouseout"
.select("text") instead of ".text".
style("pointer-events", "none) on the text to stop it causing extra mouse events when it's added.
Added fake data.
The easiest way to do this is to assign a unique ID to the new text element and select by that:
.on("mouseenter", function(d) {
d3.select(this)
.append("text")
.attr("id", "myText")
.text(function(d){return d.occurrence + " occurences"});
})
.on("mouseout", function(d) {
d3.select("#myText").remove();
});
I am trying to create a page with a scatterplot, a network diagram, and a table. I was able to get the mousehandling to work on the network diagram and the table (with the help of #Superboggly at Link D3 force layout network diagram with table on mouseover). Now I am trying to get the mousehandling to work on a second svg with a scatterplot, and I think I'm messing up the referencing.
var mapit = svg2.selectAll("maprect")
.data(graph.nodes)
.enter().append("rect")
.attr("x", function(d) { return xScale(d.long); })
.attr("y", function(d) { return yScale(d.lat); })
.attr("height", 20)
.attr("width", 20)
.attr("fill", "cyan")
// This mouseover doesn't work, what am I missing?
.on("mouseover", function(d) {
d3.select(this).select("rect").style("fill", "orange");
})
.on("mouseout", function(d) {
d3.select(this).select("rect").style("fill", "cyan");
});
I'm new to D3 and to JavaScript, and am confused by the collection of ... mapit, svg2, maprect, graph.nodes, rect, ... that I pieced together from other folks' examples. Any suggestions?
The example is posted as a jsFiddle.
You were so close! Just remove the .select("rect") in the functions:
.on("mouseover", function(d) {
d3.select(this).style("fill", "orange");
})
.on("mouseout", function(d) {
d3.select(this).style("fill", "cyan");
});
updated fiddle.
My goal is to remove a data point from my bar chart.
It will then update itself:
Update X and Y axis
Update the actual bar chart
Update the legend
Issues I am having:
When I exit().remove() the rectangles in my graph, the code also gets rid of the rectangles in the legend. When I try to enter() the rectangles in my legend, they do not appear. I am not sure what is happening here, but I am not being successful in adding and removing elements due to data changes. Any help would be appreciated.
Code snippet of where I think I am having issues:
This is the part that also deletes the rectangles in the legend (I am not sure if I should do this here or in the enter/update/delete part of the legend)
The code below is executed right after the user clicks the "delete all" button. My intent here is to only delete one bar (the one named "ALL") and update the chart.
//Select rectangles
var bars = svg.selectAll("rect")
.data(dataset, function(d) { return d.State; });
//Enter rectangles
bars.enter()
.append("rect")
.style("fill", function(d,i) { return color(i) })
.attr("x", function(d) { return xScale(d.State); })
.attr("y", function(d) { return yScale(d.CustomerCount) })
.attr("width", xScale.rangeBand()) //returns rangeRoundBands width
.attr("height", function(d) { return h - yScale(d.CustomerCount) });
//Update rectangles
bars.transition()
.duration(1000)
.style("fill", function(d,i) { return color(i) })
.attr("x", function(d) { return xScale(d.State); })
.attr("y", function(d) { return yScale(d.CustomerCount) })
.attr("width", xScale.rangeBand()) //returns rangeRoundBands width
.attr("height", function(d) { return h - yScale(d.CustomerCount) });
//Exit rectangles
bars.exit()
.transition()
.duration(500)
.attr("x", w)
.remove();
Here is entire the code:
http://plnkr.co/edit/Nue5bocQsI4E6D5wfNSP
Here is the slightly smaller code:
I tried to reduce the code as much as possible but it is still pretty big.
http://jsfiddle.net/aNQWV/
In the part where you update the data you say:
var bars = svg.selectAll("rect")
.data(dataset, function(d) { return d.State; });
The problem is that at this point the svg contains two kind of rects. One kind corresponds to the bars, and the other ones are part of the legend. This means that you are joining the new data to this mix of rects, while actually you wanted to join the new data with the bars only.
So, you need a more specific selector that targets only the bar rects. The typical approach is to add a class to those rects when you create them:
svg.selectAll(".bar")
.data(input)
.enter().append("rect")
.attr("class", "bar");
Then in the part where you update the data you would say:
var bars = svg.selectAll(".bar")
.data(dataset, function(d) { return d.State; });