Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3: .transition() not working with events

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.

like image 914
Zander Rootman Avatar asked Dec 18 '22 06:12

Zander Rootman


1 Answers

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>
like image 122
Gerardo Furtado Avatar answered Dec 24 '22 00:12

Gerardo Furtado