如何使用匿名函数正确调用D3.js中的其他函数

How to use anonymous functions to call other functions in D3.js correctly

我正在尝试使用一个匿名函数来调用另外两个函数,但实际上无法正常工作。我在 SO: Calling two functions on same click event with d3.js 上关注了这个问题,但仍然无法正确解决。

期望的效果是我的条形图同时改变条形和颜色。这就是我正在尝试的:

我要调用的两个函数:

 function selectDataset(d) {
        let value = this.value;
        if (value == "total") {
            change(datasetTotal);
        } else if (value == "option1") {
            change(datasetOption1);
        } else if (value == "option2") {
            change(datasetOption2);
        }
    }

    function changeColor(d) {
         let value = this.value;
        if (value == "total") {
            d3.selectAll("rect")
                    .transition()
                    .duration(2000)
                    .style("fill", 'blue')
        } else if (value == "option1") {
            d3.selectAll("rect")
                    .transition()
                    .duration(2000)
                    .style("fill", 'red')
        } else if (value == "option2") {
           d3.selectAll("rect")
                    .transition()
                    .duration(2000)
                    .style("fill", 'yellow')
        }

    }

我的匿名函数:


    d3.selectAll("input").on("change", function(d) {
         selectDataset.call(this, d);
         changeColor.call(this, d);
        });


这是我的更改功能:

function change(dataset) {

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

        svg.select(".y.axis").remove();
        svg.select(".x.axis").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(0)")
            .attr("x", 50)
            .attr("dx", ".1em")
            .style("text-anchor", "end")
            .text("Option %");

        var bar = svg.selectAll(".bar")
            .data(dataset, function(d) {
                return d.label;
            });

        var barExit = bar.exit().remove();

        var barEnter = bar.enter()
                        .append("g")
                        .attr("class", "bar");

        var barRects = barEnter.append("rect")
            .attr("x", function(d) {
            return x(0);
        })
        .attr("y", function(d) {
            return y(d.label);
        })
        .attr("width", function(d) {
            return x(d.value);
        })
        .attr("height", y.bandwidth());

        var barTexts = barEnter.append("text")
            .attr("x", function(d) {
                return x(d.value) + 10;
            })
            .attr("y", function(d) {
                return y(d.label) + y.bandwidth() / 2;
            })
            .attr("dy", ".35em")
            .text(function(d) {
                return d.value;
            });

        var barRectUpdate = bar.select("rect")
            .transition()
            .duration(3050)
            .attr("x", function(d) {
            return x(0);
        })
        .attr("y", function(d) {
            return y(d.label);
        })
        .attr("width", function(d) {
            return x(d.value);
        })
        .attr("height", y.bandwidth());

        var barTextsUpdate = bar.select("text")
              .transition()
              .duration(3050)
              .attr("x", function(d) {
              return x(d.value) + 10;
            })
            .attr("y", function(d) {
                return y(d.label) + y.bandwidth() / 2;
            })
            .attr("dy", ".35em")
            .text(function(d) {
                return d.value;
            });
    };

我的图表

   var margin = {
            top: (parseInt(d3.select('.area-heat-cool').style('height'), 10) / 20),
            right: (parseInt(d3.select('.area-heat-cool').style('width'), 10) / 20),
            bottom: (parseInt(d3.select('.area-heat-cool').style('height'), 10) / 20),
            left: (parseInt(d3.select('.area-heat-cool').style('width'), 10) / 5)
        },
        width = parseInt(d3.select('.area-heat-cool').style('width'), 10) - margin.left - margin.right,
        height = parseInt(d3.select('.area-heat-cool').style('height'), 10) - margin.top - margin.bottom;

    var div = d3.select(".area-heat-cool").append("div").attr("class", "toolTip");

    var y = d3.scaleBand()
        .rangeRound([height, 0], .2, 0.5)
        .paddingInner(0.1);

    var x = d3.scaleLinear()
        .range([0, width]);

    var xAxis = d3.axisBottom()
        .scale(x);

    var yAxis = d3.axisLeft()
        .scale(y);

    var svg = d3.select(".area-heat-cool").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 + ")");

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

    d3.select("input[value=\"total\"]").property("checked", true);
    change(datasetTotal);

数据集示例:

data1 = [{label: "example 1", value: 156}
{label: "example 2", value: 189}
{label: "example 3", value: 234}
{label: "example 4", value: 345}
{label: "example 5", value: 346}
{label: "example 6", value: 456}
{label: "example 7", value: 489}
{label: "example 8", value: 567}]; 


data2 = [{label: "example 1", value: 23}
{label: "example 2", value: 211}
{label: "example 3", value: 45}
{label: "example 4", value: 64}
{label: "example 5", value: 95}
{label: "example 6", value: 32}
{label: "example 7", value: 0}
{label: "example 8", value: 234}]; 

还有我的单选按钮:

<div class="area-heat-cool">
<form>
    <label><input type="radio" name="dataset" id="dataset" value="total" checked> Total</label>
    <label><input type="radio" name="dataset" id="dataset" value="option1"> Option 1</label>
    <label><input type="radio" name="dataset" id="dataset" value="option2"> Option 2</label>
</form>
</div>

当我这样做时,它以某种方式弄乱了我的数据。颜色发生变化,但条形图不再移动到正确的位置。我没有收到任何错误,但行为不是预期的。

我是否正确使用了这个匿名函数?很感谢任何形式的帮助。非常感谢

触发事件时,您对 rect 元素应用了两个转换。但是,这样做时,第二个转换会覆盖第一个转换。

然后在 change 函数中应用第一个转换,您可以在其中更改 rect 元素的大小。当 change 函数 returns 时,您立即调用 changeColor 函数,该函数 select 包含所有 rect 元素并应用不同的过渡来更改元素的颜色rect 个元素。因此颜色转换会覆盖尺寸转换。

要解决这个问题,您可以在一个地方将所有过渡应用到相同的元素。在我的示例中,我将把 changeColor 函数中的代码放入 change 函数中。

首先,更改 change 函数声明以启用包括无线电 select 值:

function change(dataset, optionSelect) {
  // code not shown
}

然后,更新 change 中执行 rect 元素转换的代码:

var barRectUpdate = bar.select("rect")
  .transition()
  .duration(3050)
  .attr("x", function(d) {
    return x(0);
  })
  .attr("y", function(d) {
    return y(d.label);
  })
  .attr("width", function(d) {
    return x(d.value);
  })
  .attr("height", y.bandwidth())
  .style('fill', function () {
    if (optionSelect === "total") {
      return 'blue'
    } else if (optionSelect === "option1") {
      return 'red'
    } else if (optionSelect === "option2") {
      return 'yellow'
    }
  });

更新 selectDataset 函数以在调用 change:

时包含无线电 select 值
function selectDataset(d) {
  let value = this.value;
  if (value === "total") {
    change(datasetTotal, value);
  } else if (value === "option1") {
    change(datasetOption1, value);
  } else if (value === "option2") {
    change(datasetOption2, value);
  }
}

删除对 changeColor 函数的调用:

d3.selectAll("input").on("change", function(d) {
  selectDataset.call(this, d);
});

最后,完全删除 changeColor 函数。

需要考虑的其他几件事:

  • id 属性应该是唯一的。现在所有 input 元素都具有相同的 id。
  • 考虑将 selectDatasetchangeColor 中的字符串值检查更改为 === 而不是 ==,以避免在比较值时发生意外类型转换:Difference between == and === in JavaScript
  • 当您之后不使用这些变量时,您不需要将所有 D3 操作存储在变量中。例如。 bar.select("rect")var barRectUpdate = bar.select("rect") 一样有效。

希望对您有所帮助!