FateJudgement FateJudgement - 4 months ago 6
Javascript Question

How do you make certain elements highlight when clicked?

I am building a graph which shows a variety of bars which each have their own specific data. The bars are colour coded and there is a legend which shows what the colours mean. What I am trying to do it make it when you click on one of the circles on the legend, the bars with the corresponding colour on the graph are highlighted.



<!DOCTYPE html>
<html>
<head>
<meta>
<meta name="description" content="Bar Graph Showing the Amount of Resources Being Used per Project " />
<meta charset="utf-8">
<title>Resources per Program</title>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

<style type="text/css">
h1 {
font-size: 35px;
color: darkblue;
font-family: Helvetica;
border-bottom-width: 3px;
border-bottom-style: dashed;
border-bottom-color: black;
}

h2 {
font-size: 20px;
color: darkred;
font-family: Helvetica;
margin-left: 900px;
margin-top: -40px;
margin-bottom: 70px
}

h3 {
font-size: 13px;
color: red;
font-weight: bold;
}

.label {
font-size: 20px;
color: darkgrey;
font-family: sans-serif;
border-bottom-width: 3px;
border-bottom-style: dashed;
border-bottom-color: black;
}

body {
background: linear-gradient(white, #a6a6a6);
}

.legend {
font-family: sans-serif;
font-size: 17px;
font-weight: normal;

}

</style>
</head>

<body>
<h1>Resources used per Program</h1>

<p>Choose Month:
<select id="label-option">
<option value="April">April</option>
<option value="May">May</option>
<option value="June">June</option>
<option value="July">July</option>
<option value="August">August</option>
<option value="September">September</option>
</select>
</p>
<h3>*Hold down the Selector and Drag onto Desired Option* (Touch Screen Users)</h3>

<script type="text/javascript">
var width = 1700
var height = 500
var emptyVar = 0
var dataArrayProjects = ['2G', 'AB', 'BC', 'CD', 'DE', 'EF', 'FG', 'GH']
var April = [2.5, 2.1, 1.3, 15.2, 1, 4, 1, 4]
var May = [2.5, 2.1, 1, 14.8, 1, 4, 2, 6]
var June = [2.5, 2.1, 1, 14.8, 1, 4, 2, 6]
var July = [2.5, 3.4, 2, 14.8, 1, 3.5, 2.5, 6]
var August = [2.5, 3.4, 2.5, 14.8, 1.2, 3.5, 2.15, 6]
var September = [2.5, 3, 2, 13.5, 1, 3.5, 2.5, 6]

d3.select("#label-option").on("change", change)

function change() {
var data = April;
if (this.selectedIndex == 1){
data = May;
} else if (this.selectedIndex == 2){
data = June;
} else if (this.selectedIndex == 3){
data = July;
} else if (this.selectedIndex == 4){
data = August;
} else if (this.selectedIndex == 5){
data = September;
}
canvas.selectAll("rect")
.data(data)
.attr("width", emptyVar)
.attr("height", 50)
.attr("fill", function(d, i) {
return color[i%8]
})
.attr("y", function(d, i) {
return i * 55
})

bars.transition()
.duration(1500)
.delay(200)
.attr("width", function(d) {
return widthScale(d);
});

texts = canvas.selectAll(".label")
.data(data)
.attr("x", function(d) {
return widthScale(d) + 10;
})
.attr("fill", function(d, i) {
return color[i%8]
})
.attr("y", function(d, i) {
return (i * 55) + 25
})
.style("opacity", 0)
.text(function(d, i) {
return d;
})

texts.transition()
.duration(1800)
.delay(180)
.style("opacity", 1)
}

var widthScale = d3.scale.linear()
.domain([0, 16])
.range([0, width - 60]);

var heightScale = d3.scale.ordinal()
.domain(dataArrayProjects)
.rangePoints([10, height - 85]);

var color = ["#1F45FC", "#87AFC7", "#87AFC7", "#151B54", "#1F45FC", "#151B54", "#1F45FC", "#151B54"]

var xAxis = d3.svg.axis()
.ticks("20")
.scale(widthScale);

var yAxis = d3.svg.axis()
.scale(heightScale)
.orient("left");

var canvas = d3.select("body")
.append("svg")
.attr("width", width + 250)
.attr("height", height)
.append("g")
.attr("transform", "translate(130, 0)");

var bars = canvas.selectAll("rect")
.data(April)
.enter()
.append("rect")
.attr("width", emptyVar)
.attr("height", 50)
.attr("fill", function(d, i) {
return color[i%8]
})
.attr("y", function(d, i) {
return i * 55
})

var texts = canvas.selectAll(".label")
.data(April)
.enter()
.append("text")
.attr("class", "label")
.attr("x", function(d) {
return widthScale(d) + 10;
})
.attr("fill", function(d, i) {
return color[i%8]
})
.attr("y", function(d, i) {
return (i * 55) + 25
})
.text(function(d, i) {
return d;
})
.style("opacity", 0)

canvas.append("g")
.attr("transform", "translate(0, 430)")
.attr("font-family", "Helvetica")
.attr("font-size", "15px")
.call(xAxis);

canvas.append("g")
.attr("font-family", "Helvetica")
.attr("font-size", "15px")
.style("fill", "black")
.attr("class", "y axis")
.call(yAxis);

bars.transition()
.duration(1500)
.delay(200)
.attr("width", function(d) {
return widthScale(d);
})

texts.transition()
.duration(1800)
.delay(180)
.style("opacity", 1)

var yAxisLine = canvas.append("line")
.attr("x1", -3)
.attr("y1", 0)
.attr("x2", -3)
.attr("y2", 436)
.attr("stroke-width", 6)
.attr("stroke", "black");

var legend = canvas.append("g")
.attr("class", "legend")
.attr("width", 450)
.attr("height", 300)
.attr("x", 1000)
.attr("y", 0)

legend.append("text")
.text("All Projects")
.attr("fill", "black")
.attr("stroke-width", 1)
.attr("x", 1300)
.attr("y", 20)
legend.append("text")
.text("All Core Capabilities")
.attr("fill", "black")
.attr("stroke-width", 1)
.attr("x", 1300)
.attr("y", 50)
legend.append("text")
.text("Projects and Core Capabilities")
.attr("fill", "black")
.attr("stroke-width", 1)
.attr("x", 1300)
.attr("y", 80)

legend.append("circle")
.attr("r", 8)
.attr("fill", "#1F45FC")
.attr("cx", 1275)
.attr("cy", 14.5)
legend.append("circle")
.attr("r", 8)
.attr("fill", "#87AFC7")
.attr("cx", 1275)
.attr("cy", 44.5)
legend.append("circle")
.attr("r", 8)
.attr("fill", "#151B54")
.attr("cx", 1275)
.attr("cy", 74.5)

</script>
<h2>Resources</h2>
</body>
</html>





Excuse the extremely high width, it is made to fit on a large touch screen which I am using.

What I am asking is how do I make it when you click on one of the circles in the legend, all bars with the same colour as that circle highlight (as in the stroke not the entire bar).

Thank you in advance!

Answer

It is a little difficult to select the corresponding bars because the colors of your bars, right now, don't depend on any data, they are assigned by an index:

.attr("fill", function(d, i) {
        return color[i%8] 
      })

If the colors were set by data (and that is the best approach...), it would be simply a matter of setting a class and highlighting them by that class.

But there is a way to select all the bars with the same color of the clicked circle: we can select all the bars, and then filtering the selection by attribute:

.on("click", function(){
            var rects = d3.selectAll("rect").filter(function(){
            return this.attributes.fill.nodeValue == "#1F45FC";
            });
 }); 

Here is the fiddle: https://jsfiddle.net/gerardofurtado/fxqrd3gb/ (I didn't use SO snippet because the bars are too long)

Click on the circle and the bars with the same color will have a black stroke (width 2). Click it again and the stroke is removed.

Comments