D3 color change on mouseover using classed(“active”,true)

≡放荡痞女 提交于 2019-12-06 02:11:50

问题


I'm new to js and D3. I've generated a heatmap of-sorts and would like to change the color of a tile using D3's on.mouseover. I'm able to change the color explicitly but want to use a CSS active rule. Probably something simple to fix. Any help would be greatly appreciated. The full code is below.

Thanks.

<!DOCTYPE html>
<meta charset="utf-8">
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<title>MJ-Heatmap</title>
<header>
<H1>Country By District_Port_Nme Heatmap</H1>
<p></p>
</header>   
<style>
body {
font: 10px sans-serif;
}
.label {
font-weight: bold;
}
.tile {
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<!-- CSS active state not working -- >
.tile active {
fill: red;
}
</style>
</head>
<body>
<script type="text/javascript">
var buckets = [
{country:"Brazil", distrinct_port_nme:"Baltimore, Maryland", sum_teu: 2, sum_weight: 18585, count_shipments: 1}, 
{country:"Colombia", distrinct_port_nme:"Baltimore, Maryland", sum_teu:28, sum_weight:258028, count_shipments:11}, 
{country:"Brazil", distrinct_port_nme:"Chicago, Illinois", sum_teu: 2, sum_weight: 18585, count_shipments: 1}, 
{country:"Colombia", distrinct_port_nme:"Houston, Texas", sum_teu:14, sum_weight: 95995, count_shipments: 7}, 
{country:"Brazil", distrinct_port_nme:"Long Beach, California", sum_teu: 2, sum_weight: 18584, count_shipments: 1}, 
{country:"China", distrinct_port_nme:"Long Beach, California", sum_teu: 2, sum_weight: 19180, count_shipments: 1}, 
{country:"Colombia", distrinct_port_nme:"Long Beach, California", sum_teu:12, sum_weight:117873, count_shipments: 6}, 
{country:"Singapore", distrinct_port_nme:"Long Beach, California", sum_teu: 6, sum_weight: 77176, count_shipments: 4}, 
{country:"Belgium", distrinct_port_nme:"Los Angeles, California", sum_teu: 2, sum_weight: 17780, count_shipments: 1}, 
{country:"Brazil", distrinct_port_nme:"Los Angeles, California", sum_teu: 2, sum_weight: 18584, count_shipments: 1}, 
{country:"Colombia", distrinct_port_nme:"Los Angeles, California", sum_teu: 7, sum_weight: 52046, count_shipments: 5}, 
{country:"Hong Kong", distrinct_port_nme:"Los Angeles, California", sum_teu: 2, sum_weight: 19180, count_shipments: 1}, 
{country:"India", distrinct_port_nme:"Los Angeles, California", sum_teu:48, sum_weight:460563, count_shipments:24}, 
{country:"Singapore", distrinct_port_nme:"Los Angeles, California", sum_teu:12, sum_weight:115384, count_shipments: 6}, 
{country:"Brazil", distrinct_port_nme:"New York, New York", sum_teu: 4, sum_weight: 27032, count_shipments: 2}, 
{country:"Colombia", distrinct_port_nme:"New York, New York", sum_teu: 8, sum_weight: 42885, count_shipments: 4}, 
{country:"India", distrinct_port_nme:"New York, New York", sum_teu:14, sum_weight:129116, count_shipments: 7}, 
{country:"Singapore", distrinct_port_nme:"New York, New York", sum_teu:42, sum_weight:560628, count_shipments:27}, 
{country:"Brazil", distrinct_port_nme:"Newark, New Jersey", sum_teu: 2, sum_weight: 18584, count_shipments: 1}, 
{country:"Colombia", distrinct_port_nme:"Newark, New Jersey", sum_teu:83, sum_weight:785372, count_shipments:42}, 
{country:"India", distrinct_port_nme:"Newark, New Jersey", sum_teu:62, sum_weight:587654, count_shipments:31}, 
{country:"Brazil", distrinct_port_nme:"Norfolk, Virginia", sum_teu: 8, sum_weight: 33622, count_shipments: 4}, 
{country:"India", distrinct_port_nme:"Norfolk, Virginia", sum_teu: 2, sum_weight: 17780, count_shipments: 1}, 
{country:"Brazil", distrinct_port_nme:"Philadelphia, Pennsylvania", sum_teu: 2, sum_weight: 20160, count_shipments: 1}
];
// margins
var margin = {top: 20, right: 90, bottom: 30, left: 100, left_padding: 100},
width = 700 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// axes and color scale
var x = d3.scale.ordinal()
.rangeBands([0, width], .2),
y = d3.scale.ordinal()
.rangeBands([height,0], .2),
z = d3.scale.linear().range(["lightgrey", "steelblue"]);
var xStep = .5,
yStep = 50;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + 0 + "," + margin.top + ")");
// Compute the scale domains.
x.domain(buckets.map(function(d){return d.country}));
y.domain(buckets.map(function(d){return d.distrinct_port_nme}));
z.domain([0, d3.max(buckets.map(function(d){return d.count_shipments}))]);
// Display the tiles for each bucket.
svg.selectAll(".tile")
.data(buckets)
.enter().append("rect")
.attr("class", "tile")
.attr("x", function(d) { return x(d.country) + margin.left+margin.left_padding; })
.attr("y", function(d) { return y(d.distrinct_port_nme); })
.attr("width",x.rangeBand())
.attr("height",y.rangeBand())
.style("stroke","goldenrod")
.style("fill", function(d) { return z(d.count_shipments); })
.on("mouseover", function() { d3.select(this).classed("active", true ) })       // classed("active",boolean) not working
.on("mouseout",  function() { d3.select(this).classed("active", false) });
//  .on("mouseover", function() { d3.select(this).style("fill", "aliceblue");});    // this change work fine
//  .on("mouseout", function(){d3.select(this).style("fill", "white");});           // this change work fine
svg.selectAll("text")
.data(buckets)
.enter().append("text")
.text("text")
.attr("x", function(d) { return x(d.country) + margin.left + margin.left_padding + x.rangeBand()/2; })
.attr("y", function(d) { return y(d.distrinct_port_nme) + y.rangeBand()/2; })
.attr("dx",0)
.attr("dy",".35em")
.attr("text-anchor","middle")
.style("fill","black").attr("font-size","14").attr("font-weight","Bold")
.text( function(d) { return d.count_shipments; });
// Add a legend for the color values.
var legend = svg.selectAll(".legend")
.data(z.ticks(6).slice(1).reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(" + 0 + "," + (10 + i * 20) + ")"; });
legend.append("rect")
.attr("width", 20)
.attr("height", 20)
.style("fill", z);
legend.append("text")
.attr("x", 26)
.attr("y", 10)
.attr("dy", ".35em")
.text(String);
svg.append("text")
.attr("class", "label")
.attr("x", 0)
.attr("y", 0)
.attr("dy", ".35em")
.text("count_shipments");
// Add an x-axis with label.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+ (margin.left+margin.left_padding) + "," + height + ")")
.attr("text_anchor", "top")
.call(d3.svg.axis().scale(x).orient("bottom"))
.append("text")
.attr("class", "label")
.attr("x", width-10)
.attr("y", -5)
.attr("text-anchor", "end")
.text("Country");
// Add a y-axis with label.
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate("+ (margin.left+margin.left_padding) + "," + 0 + ")")
.attr("text-anchor","right")
.call(d3.svg.axis().scale(y).orient("left"))
.append("text")
.attr("class", "label")
.attr("y", 3)
.attr("dy", ".71em")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
.text("distrinct_port_nme");
</script>
</body>
</html>  

回答1:


EDIT

First off, you were right that your CSS is slightly off.

.tile active {
  fill: red;
}

should be:

.tile.active {
  fill: red;
}

Then, sort of like I explained below, the fill you're applying to the element with

.style("fill", function(d) { return z(d.count_shipments); })

ends up taking precedence over the fill applied by the active class.

However, unlike what I initially suggested, you can work around it by simply swapping style with attr, so you need:

.attr("fill", function(d) { return z(d.count_shipments); })

(probably should do this for stroke as well).

Here's an updated, working jsFiddle


ORIGINAL POST

I suspect it works the way it should –– i.e. class "active" with its associated fill is being added/removed appropriately –– and you'd be able to verify this by right-clicking and inspecting the element in dev tools.

I think the real problem is that you're also setting a fill directly on the element (one line before your .on("mouseover"...) call), which ALWAYS overrides the fill applied by your class. Generally in CSS you could (not saying you should) use the !important keyword to force it to accept the fill applied by the class. But I'm pretty sure that won't work with SVG (as opposed to normal HTML elements).

So, I think your only option is:

.style("fill", function(d) { return z(d.count_shipments); })
.on("mouseover", function() {
  d3.select(this)
    .attr('fill', '') // Un-sets the "explicit" fill (might need to be null instead of '')
    .classed("active", true ) // should then accept fill from CSS
})
.on("mouseout",  function() {
  d3.select(this)
    .classed("active", false)
    .attr('fill', function(d) { return z(d.count_shipments); }) // Re-sets the "explicit" fill
  });

Hope this works....



来源:https://stackoverflow.com/questions/15709304/d3-color-change-on-mouseover-using-classedactive-true

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!