下拉选择后,条形图未更新-d3.js

时间:2018-09-10 16:05:48

标签: javascript d3.js

我是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;});

还是,我将投票并检查能解释为什么是这种情况的答案……谢谢!

1 个答案:

答案 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选择中,即optionselect个元素的数组。在下拉列表中选择其他元素时,这些项目不会触发change事件。即使他们这样做,此代码也可以:

    var selectedUnit = d3.select(this)
        .select("select")
        .property("value");

select中搜索this元素,对于事件侦听器,该元素是触发事件的元素。如果thisoption元素,则其中没有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);
});