为什么窗口调整大小事件会重复添加g元素?

时间:2019-02-06 06:09:43

标签: javascript jquery d3.js

当我尝试使用D3调整事件大小时,它表明我在图表中附加的元素“ g”在调整大小期间正在重复打印。如何使用this关键字?或类似的东西? This is the code on js fiddle

问题:如何在d3中使用this关键字解决此问题?

function redraw(){
    let chartContainer = document.getElementById("bar_Chart");
    let containerWidth = chartContainer.clientWidth;
    let containerHeight = chartContainer.clientHeight;


    let inputData = [
        {name: "JAN", value:  10},
        {name: "FEB", value:  8},
        {name: "MAR", value: 15},
        {name: "APR", value: 16},
        {name: "MAY", value: 23},
        {name: "JUN", value: 30},
        {name: "JUL", value: 12},
        {name: "AUG", value: 41},
        {name: "SEP", value: 52},
        {name: "OCT", value: 23},
        {name: "NOV", value: 32},
        {name: "DEC", value: 42}
    ];

    let margin = {top: 20, right: 30, bottom: 30, left: 40};
    let width = containerWidth - margin.left - margin.right;
    let height = containerHeight - margin.top - margin.bottom;
        let x = d3.scaleBand()
        .rangeRound([0, width])
        .padding(0.1);

    let y = d3.scaleLinear()
        .range([height, 0]);

    let tooltip = d3.select("body").append("div").attr("class", "toolTip");


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

    x.domain(inputData.map(function(d) { return d.name; }));
    y.domain([0, d3.max(inputData, function (d) { return d.value; })]);
    let xAxis = d3.axisBottom(x);

    let yAxis = d3.axisLeft(y);

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



    chart.append("g")
        .attr("class", "y axis")
        .call(yAxis)


    chart.selectAll(".bar")
        .data(inputData)
        .enter().append("rect")
        .attr("class", "bar")
        .attr("x", function(d) { return x(d.name); })
        .attr("y", function(d) { return y(d.value); })
        .attr("height", function(d) { return height - y(d.value); })
        .attr("width", x.bandwidth())
        .on("mouseover", function (d) {
            tooltip.transition()
                .duration(200)
                .style("opacity", 0.85)
                .style("left", d3.event.pageX - 50 + "px")
                .style("top", d3.event.pageY - 70 + "px");
            tooltip
                .style("display", "inline-block")
                .html((d.name) + "£" + (d.value));
            d3.select(this).transition()
            .duration(300)
            .style("fill", "red");
        })
        .on("mouseout", function () {
            tooltip.transition()
                .duration(200)
                .style("opacity", 0);
            tooltip
                .style("display", "none");
            d3.select(this).transition()
                .duration(300)
                .style("fill", "steelblue")
                .style("transition", "scale(1.8)");
        });

    }


    redraw();
    window.addEventListener("resize", redraw);

1 个答案:

答案 0 :(得分:0)

首次绘制图形时,需要调用enter将数据绑定到DOM元素,并调用append创建DOM元素。调整窗口大小时,您不再希望调用enterappend,因为数据已经绑定到DOM元素并且DOM元素已经存在。您真正想要做的就是使用attr调整事物的大小并重新设置轴。

您可以拥有两个函数drawredraw,它们与draw相同,只是它不调用enterappend。我在这里显示它们:

let inputData = [
    {name: "JAN", value:  10},
    {name: "FEB", value:  8},
    {name: "MAR", value: 15},
    {name: "APR", value: 16},
    {name: "MAY", value: 23},
    {name: "JUN", value: 30},
    {name: "JUL", value: 12},
    {name: "AUG", value: 41},
    {name: "SEP", value: 52},
    {name: "OCT", value: 23},
    {name: "NOV", value: 32},
    {name: "DEC", value: 42}
];

let margin = {top: 20, right: 30, bottom: 30, left: 40};

function draw() {
    let chartContainer = document.getElementById("bar_Chart"); 
    let containerWidth = chartContainer.clientWidth; 
    let containerHeight = chartContainer.clientHeight;

    let width = containerWidth - margin.left - margin.right;
    let height = containerHeight - margin.top - margin.bottom;

    let x = d3.scaleBand()
        .rangeRound([0, width])
        .padding(0.1);

    let y = d3.scaleLinear()
        .range([height, 0]);

    let tooltip = d3.select("body").append("div").attr("class", "toolTip");

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

    x.domain(inputData.map(function(d) { return d.name; }));
    y.domain([0, d3.max(inputData, function (d) { return d.value; })]);
    let xAxis = d3.axisBottom(x);

    let yAxis = d3.axisLeft(y);

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

    chart.append("g")
        .attr("class", "y axis")
        .call(yAxis)

    chart.selectAll(".bar")
        .data(inputData)
        .enter().append("rect")
        .attr("class", "bar")
        .attr("x", function(d) { return x(d.name); })
        .attr("y", function(d) { return y(d.value); })
        .attr("height", function(d) { return height - y(d.value); })
        .attr("width", x.bandwidth())
        .on("mouseover", function (d) {
            tooltip.transition()
                .duration(200)
                .style("opacity", 0.85)
                .style("left", d3.event.pageX - 50 + "px")
                .style("top", d3.event.pageY - 70 + "px");
            tooltip
                .style("display", "inline-block")
                .html((d.name) + "£" + (d.value));
            d3.select(this).transition()
            .duration(300)
            .style("fill", "red");
        })
        .on("mouseout", function () {
            tooltip.transition()
                .duration(200)
                .style("opacity", 0);
            tooltip
                .style("display", "none");
            d3.select(this).transition()
                .duration(300)
                .style("fill", "steelblue")
                .style("transition", "scale(1.8)");
        });
}

function redraw() { 
    let chartContainer = document.getElementById("bar_Chart"); 
    let containerWidth = chartContainer.clientWidth; 
    let containerHeight = chartContainer.clientHeight;
    let width = containerWidth - margin.left - margin.right;
    let height = containerHeight - margin.top - margin.bottom;

    let x = d3.scaleBand()
        .rangeRound([0, width])
        .padding(0.1);

    let y = d3.scaleLinear()
        .range([height, 0]);

    let tooltip = d3.select("body").append("div").attr("class", "toolTip");

    let chart = d3.select(".chart-svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    x.domain(inputData.map(function(d) { return d.name; }));
    y.domain([0, d3.max(inputData, function (d) { return d.value; })]);

    let xAxis = d3.axisBottom(x);
    let yAxis = d3.axisLeft(y);

    chart.select(".x.axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)

    chart.select(".y.axis")
        .call(yAxis)

    chart.selectAll(".bar")
        .data(inputData)
        .attr("class", "bar")
        .attr("x", function(d) { return x(d.name); })
        .attr("y", function(d) { return y(d.value); })
        .attr("height", function(d) { return height - y(d.value); })
        .attr("width", x.bandwidth())
}

document.addEventListener('DOMContentLoaded', draw);
window.addEventListener("resize", redraw);

仍然存在问题。 X方向与窗口的宽度很好地缩放,但Y方向却不。我相信您不应该在let containerHeight = chartContainer.clientHeight;函数中使用redraw,因为它不能正确计算容器元素的高度(无论窗口是增大还是缩小,它都会增加)。我会把这个留给你修复。我改而玩了window.innerHeight,这似乎效果更好,但是它计算的是窗口的高度,而不是容器div的高度。