D3: .transition() not working with events

久未见 提交于 2020-01-14 11:51:28

问题


I'm busy plotting a bar chart using D3 in Angular4.

// Enter Phase
this.chart.selectAll('.bar')
  .data(this.barData)
  .enter()
  .append('rect')
  .attr('class', 'bar');

// Update Phase
let bars = this.chart.selectAll('.bar').transition()
  .attr('x', (d) => {return this.x(this.parseTime(d.date.toUpperCase()));})
  .attr('y', (d) => {return this.y(d.point)})
  .attr('width', 15)
  .attr('height', (d) => {return this.charDimensions.height - this.y(d.point);})
    .on("mouseover", function (d) {D3.select(this).style('opacity', 0.5);})
    .on("mouseout", function (d) {D3.select(this).style('opacity', 1);})
    .on("click", (d) => {this.barClicked.emit(d);});

// Exit phase
this.chart.selectAll('.bar')
  .data(this.barData)
  .exit().remove();

In my plotting method, when I call animate() I get an error: Error: unknown type: mouseover. Which makes sense, since I'm probably trying to call the on("<event>") on a the transition returned by D3, the transition effect is there, however, everything after the transition breaks, i.e: The animation works, but the plotting is broken ofc.

However when I attempt to do the following:

// Update Phase
let bars = this.chart.selectAll('.bar');
bars.transition();
bars.attr('x', (d) => {return this.x(this.parseTime(d.date.toUpperCase()));})
  .attr('y', (d) => {return this.y(d.point)})
  .attr('width', 15)
  .attr('height', (d) => {return this.charDimensions.height - this.y(d.point);})
    .on("mouseover", function (d) {D3.select(this).style('opacity', 0.5);})
    .on("mouseout", function (d) {D3.select(this).style('opacity', 1);})
    .on("click", (d) => {this.barClicked.emit(d);});

No errors occur, but nothing happens, there is no transition effect to the new data set.


回答1:


The problem here is a confusion between two different methods that use the same name:

  • selection.on(typenames[, listener])
  • transition.on(typenames[, listener])

Since you have a transition selection, when you write...

.on(...

The method expects to see three things (or typenames):

  • "start"
  • "end"
  • "interrupt"

However, "mouseover", "mouseout" or "click" are none of them. And then you get your error...

> Uncaught Error: unknown type: mouseover

Solution:

Bind the event listeners to a regular selection. Then, after that, create your transition selection.

Therefore, in your case, bind all the on listeners to the "enter" selection (which, by the way, makes more sense), removing them from the update selection.

Have a look at this little demo. First, I create a regular, enter selection, to which I add the event listener:

var bars = svg.selectAll(null)
    .data(data)
    .enter()
    .append("rect")
    //some attr here
    .on("mouseover", function(d) {
        console.log(d)
    });

Then, I add the transition:

bars.transition()
    .duration(1000)
    etc...

Hover over the bars to see the "mouseover" working:

var svg = d3.select("svg");
var data = [30, 280, 40, 140, 210, 110];
var bars = svg.selectAll(null)
  .data(data)
  .enter()
  .append("rect")
  .attr("x", 0)
  .attr("width", 0)
  .attr("y", function(d, i) {
    return i * 20
  })
  .attr("height", 18)
  .attr("fill", "teal")
  .on("mouseover", function(d) {
    console.log(d)
  });

bars.transition()
  .duration(1000)
  .attr("width", function(d) {
    return d
  })
.as-console-wrapper { max-height: 25% !important;}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>


来源:https://stackoverflow.com/questions/44495524/d3-transition-not-working-with-events

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!