我是d3.js(和一般而言的javascript)的新手,我不确定为什么在选择下拉列表后图表没有变化。我认为在下拉选择中选择的值未达到更新功能。任何帮助表示赞赏。预先感谢...
html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 chart bar with drop down menu</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
</style>
</head>
<body>
<div id = "container">
<div id = "dropdown"></div>
<div id = "graph"></div>
<script src ="bar.js"></script>
</div>
</body>
</html>
如前所述,我的最佳猜测是unitMenu
对象中没有注册任何东西(应该将新的选择值输入到图形中)。原因是如果我在return console.log("hello, world");
函数中执行unitMenu.on('change', ...)
,控制台上将什么也没有显示。任何帮助表示赞赏:
// margins
var margin = {bottom: 30};
// padding
var padding = 20;
//Width and height
var w = 600;
var h = 250 - margin.bottom;
// create svg
var svg = d3.select("#graph")
.append("svg")
.attr("width", w)
.attr("height", h + margin.bottom);
/* section 2: import csv data and draw elements*/
// import csv data
d3.csv("data.csv").then((data, error) => {
if (error) throw error;
// create objects that "live" outside the function that will create the initial graph...
// formatting the loaded data can live outisde the graph funciton...
//format data
data.forEach((d) => {
d.group = d.group;
d.year = +d.year;
d.value = +d.value;
});
// make an array with the unitoptions for dropdown menu
unitOptions = data.map((a) => a.group).filter((item, i, ar) => {
return ar.indexOf(item) == i;
})
// create dropwdown and populate menu
var unitMenu = d3.select("#dropdown")
.append("select")
.selectAll("option")
.data(unitOptions)
.enter()
.append("option")
.attr("value", (d) => {return d;})
.text((d) => {return d;});
// function for the initial graph
var initialGraph = function(unitSelected) {
var selections = [unitSelected, "BASELINE"];
barData = data.filter(d => {return selections.includes(d.group);})
// bar chart horizontal scale
var xScale = d3.scaleBand()
.domain(d3.range(barData.filter(d => {return d.group == unitSelected;}).length))
.rangeRound([0, w])
.paddingInner(0.05); // padding inner sets the outer padding to the specified value
// bar chart vertical scale
var yScale = d3.scaleLinear()
.domain([0, d3.max(barData, d => {return d.value;})])
.range([padding, h - padding]);
// there is only a bottom axis with the tick labels for the years displayed
var xAxis = d3.axisBottom(xScale)
.tickFormat(function(d,i){return i;})// very important function, tickFormat allow us to relabel ticks...
.tickSize([0,0]); // approximate labels and bars...
// draw bottom axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0, " + (h + margin.bottom * 1/10) + ")")
.style("font-size", "12px")
.call(xAxis)
.selectAll("path, line").remove();
//Create bars
svg.selectAll("rect")
.data(barData.filter(d => {return d.group == unitSelected;}))
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d.value);
})
.attr("width", xScale.bandwidth())
.attr("height", function(d) {
return yScale(d.value);
})
.attr("fill", "hotpink");
// add labels to bars
svg.selectAll(".textBar")
.data(barData.filter(d => {return d.group == unitSelected;}))
.enter()
.append("text")
.text(d => {
x = d.value * 100;
x = +x.toFixed(1);
return x + "%";
})
.attr("text-anchor", "middle")
.attr("x", (d, i) => {
return xScale(i) + xScale.bandwidth() / 2;
})
.attr("y", (d) => {
if(h - yScale(d.value) + 0.05*h > h - 0.08*h + 0.05*h){
return h - yScale(d.value) - 0.02*h;
} else {
return h - yScale(d.value) + 0.05*h;
};
})
.attr("font-size", "11px")
.attr("fill", function(d){
if(h - yScale(d.value) + 0.05*h > h - 0.08*h + 0.05*h){
return "black";
} else {
return "white";
};
})
.attr("font-weight", "bold");
// draw comparator
svg.selectAll(".point")
.data(barData.filter(d => {return d.group == "BASELINE";}))
.enter()
.append("path")
.attr("class", "point")
.attr("fill", "steelblue")
.attr("stroke", "black")
.attr("stroke-width", "0.5")
.attr("d", d3.symbol().type(d3.symbolCross))
.attr("transform", function(d, i) {return "translate(" +
(xScale(d.year - 1) + (xScale.bandwidth() / 2)) + "," + (h - yScale(d.value)) + ")";});
};
initialGraph("A");
// function that updates data and graph
var updateGraph = function(unitSelected) {
// filter the data to include only the unit of interest
// selection of baseline and comparator
var selections = [unitSelected, "BASELINE"]; // notice that hte baseline is fixed (for now!!)
// dataset for bars
barData = data.filter(d => {return selections.includes(d.group);})
// update rect
svg.selectAll("rect")
.data(barData.filter(d => {return d.group == unitSelected;}))
.transition()
.duration(1000)
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d.value);
})
.attr("width", xScale.bandwidth())
.attr("height", function(d) {
return yScale(d.value);
})
.exit().remove();
// update labels
svg.selectAll(".textBar")
.data(barData.filter(d => {return d.group == unitSelected;}))
.transition()
.duration(1000)
.text(function(d) {
x = d.value * 100;
x = +x.toFixed(1);
return x + "%";
})
.attr("x", function(d, i) {
return xScale(i) + xScale.bandwidth() / 2;
})
.attr("y", function(d) {
if(h - yScale(d.value) + 0.05*h > h - 0.08*h + 0.05*h){
return h - yScale(d.value) - 0.02*h;
} else {
return h - yScale(d.value) + 0.05*h;
};
})
.attr("fill", function(d){
if(h - yScale(d.value) + 0.05*h > h - 0.08*h + 0.05*h){
return "black";
} else {
return "white";
};
});
};
unitMenu.on('change', function() {
// find which unit was selected from the dropdown
var selectedUnit = d3.select(this)
.select("select")
.property("value");
// run update with selected unit
updateGraph(selectedUnit);
});
});
数据:
group,year,value
A,1,0.830798527
A,2,0.14806798
B,1,0.248585574
B,2,0.902224423
C,1,0.386217747
C,2,0.526020182
D,1,0.951627372
D,2,0.936993723
BASELINE,1,0.564061688
BASELINE,2,0.337876435
好的,所以我坚持不懈地抓住了错误...
此方块...
var unitMenu = d3.select("#dropdown")
.append("select")
.selectAll("option")
.data(unitOptions)
.enter()
.append("option")
.attr("value", (d) => {return d;})
.text((d) => {return d;});
应该是...
var unitMenu = d3.select("#dropdown")
unitMenu.append("select")
.selectAll("option")
.data(unitOptions)
.enter()
.append("option")
.attr("value", (d) => {return d;})
.text((d) => {return d;});
还是,我将投票并检查能解释为什么是这种情况的答案……谢谢!
答案 0 :(得分:1)
您的修复程序起作用的原因在这里:
unitMenu.on('change', function() {
// find which unit was selected from the dropdown
var selectedUnit = d3.select(this)
.select("select")
.property("value");
// run update with selected unit
updateGraph(selectedUnit);
});
您要将change
事件侦听器附加到包含已更正版本中的下拉列表的整个div
上,而之前,它已附加到数据绑定调用中的enter选择中,即option
中select
个元素的数组。在下拉列表中选择其他元素时,这些项目不会触发change
事件。即使他们这样做,此代码也可以:
var selectedUnit = d3.select(this)
.select("select")
.property("value");
在select
中搜索this
元素,对于事件侦听器,该元素是触发事件的元素。如果this
是option
元素,则其中没有select
元素!
您还可以通过将select
元素分配给unitMenu
然后更改处理事件的代码来生成工作代码:
var unitMenu = d3.select("#dropdown")
.append("select");
unitMenu
.selectAll("option")
.data(unitOptions)
.enter()
.append("option")
.attr("value", (d) => {return d;})
.text((d) => {return d;});
[...]
unitMenu.on('change', function() {
var selectedUnit = d3.select(this) // this is the select element
.property("value");
updateGraph(selectedUnit);
});