1

我有一个 Django View 将 tablib CSV 提供给 D3.js。但是,我发现无论我如何或在何处使用 exit(),它都不会返回任何内容——因此,我无法删除旧的、不需要的元素,并且所有内容都只是相互叠加。

有任何想法吗?我的代码非常依赖于基本的堆积条形图示例

<div id="id_d3_canvas" class="d3_canvas_space">
</div>
<div>
<form id="id_date_form">
    <input id="id_date_small" name="date1" type="text" value="03-01-2012">
    <input id="id_date_large" name="date2" type="text" value="{% now 'n-j-Y' %}">
    <select name="our_people" multiple>
        {% for person in object_list %}
        <option value="{{ person.name }}" selected>{{ person.name }}</option>
        {% endfor %}
    </select>
</form>
<button id="id_test_data_gather" class="btn btn-primary update_d3_csv">Update</button>
</div>

<script src="{{ STATIC_URL }}cms/js/d3.min.js"></script>
<script>
    var margin = {top: 20, right: 20, bottom: 30, left: 40},
        width = 900 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;

    var x = d3.scale.ordinal()
        .rangeRoundBands([0, width], .1);

    var y = d3.scale.linear()
        .rangeRound([height, 0]);

    var color = d3.scale.ordinal()
        .range(["#98abc5", "#ff8c00"]);

    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom");

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left")
        .tickFormat(d3.format(".2s"));

    var svg = d3.select(".d3_canvas_space").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var date_info = $("#id_date_form").serialize(),
        initial_csv_url = "{% url blahblah %}?" + date_info;


    function updateMultiData(multi_csv_url) { 
        d3.csv(multi_csv_url, function(error, data) {
            color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Name"; }));

            data.forEach(function(d) {
                var y0 = 0;
                d.tasks = color.domain().map(function(category) { return {category: category, y0: y0, y1: y0 += +d[category]};});
                d.total_tasks = d.tasks[d.tasks.length - 1].y1;
            });

            data.sort(function(a, b) { return b.total - a.total; });

            x.domain(data.map(function(d) { return d.Name; }));
            y.domain([0, d3.max(data, function(d) { return d.total_tasks; })]);

            svg.selectAll(".name").data(data).exit().remove()

            svg.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + height + ")")
                .call(xAxis);

            svg.append("g")
                .attr("class", "y axis")
                .call(yAxis)
                .append("text")
                .attr("transform", "rotate(-90)")
                .attr("y", 6)
                .attr("dy", ".71em")
                .style("text-anchor", "end")
                .text("Tasks");

            var person_name = svg.selectAll(".name")
                .data(data)
                .enter().append("g")
                .attr("transform", function(d) { return "translate(" + x(d.Name) + ",0)"; });

            person_name.selectAll("rect")
                .data(function(d) { return d.tasks; })
                .enter().append("rect")
                .attr("width", x.rangeBand())
                .attr("y", function(d) { return y(d.y1); })
                .attr("height", function(d) { return y(d.y0) - y(d.y1); })
                .style("fill", function(d) { return color(d.category); });

            console.log(svg.selectAll(".name").data(data).exit());

            var legend = svg.selectAll(".legend")
                .data(color.domain().slice().reverse())
                .enter().append("g")
                .attr("class", "legend")
                .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

            legend.append("rect")
                .attr("x", width - 18)
                .attr("width", 18)
                .attr("height", 18)
                .style("fill", color);

            legend.append("text")
                .attr("x", width - 24)
                .attr("y", 9)
                .attr("dy", ".35m")
                .style("text-anchor", "end")
                .text(function(d) { return d; });

        });
    };

    updateMultiData(initial_csv_url);

    $(document).on("click", "button.update_d3_csv", function(e){
        e.preventDefault();
        var new_info = $("#id_date_form").serialize(),
            new_multi_csv_url = "{% url blahblah %}?" + new_info;
        updateMultiData(new_multi_csv_url);
        console.log(new_multi_csv_url);
    });
</script>
4

1 回答 1

3

在上面的示例中,输入的节点似乎没有定义的类。任何后续d3.selectAll(".name")都将返回一个空选择,并且所有数据元素都将显示在.enter()方法下。

每次附加输入节点时,您可能想尝试分配相应的类名:

.enter().append("g").classed("name",true)

您可能还需要考虑使用 的第二个参数.data()为每个数据点定义一个唯一标识符(键),以确保在每次更新时退出正确的元素(如果顺序不同)。 https://github.com/mbostock/d3/wiki/Selections#wiki-data

在您的代码中,“名称”属性可能用作键:

var person_name = svg.selectAll(".name")
            .data(data,function(d) { return d.name; })

最后我注意到您在更新函数中附加了轴。这意味着在之前的更新之上,每次更新都会附加一组新的轴。您可能希望将它们移到顶层。

于 2013-03-20T21:25:05.517 回答