Event handling

selection.on()


const countries = [
  {name: "Germany", population: 84_270_625},
  {name: "France", population: 68_042_591},
  {name: "Spain", population: 47_222_613},
  {name: "the Netherlands", population: 17_882_900},		
  {name: "Sweden", population: 10_481_937},	
];

const scaleFactor = 1/1000000;
const largestPopulation = d3.max(countries, d => d.population);
const display = d3.select("#display");

const svg = d3.select("body")
  .insert("svg", "#display")
  .attr("width", "100%")
  .attr("height", "100%")
  .attr("viewBox", `0 0 ${countries.length * (Math.sqrt(scaleFactor * largestPopulation) + 1)} ${Math.sqrt(scaleFactor * largestPopulation)}`);

const rects = svg.selectAll("rect")
  .data(countries)
  .join("rect")
    .attr("x", (d, i) => i * (Math.sqrt(scaleFactor * largestPopulation) + 1) )	
    .attr("width", (d, i) => Math.sqrt(scaleFactor * d.population) )
    .attr("height", (d, i) => Math.sqrt(scaleFactor * d.population) )			
	.on("click", (e, d) => display.text(`You clicked on ${d.name}`) )
	.on("mouseover", function (e, d) {
	  d3.select(this)
	    .style('fill', 'orange');
	})
	.on("mouseout", function (e, d) {
	  d3.select(this)
	    .style('fill', 'steelblue');
	});

Result:

Click on a square...

Note that this is a DOM element and not a D3 selection; it needs d3.select(this) first to turn it into a selection. And also note that this in an arrow function event handler refers to the global object (or to undefined in strict mode).

pointer()

Next example uses the native JavaScript properties pageX and pageY of the MouseEvent interface.


const vehicles = [
  { name: "Dump truck", pathAttr: "M0 …" },
  { name: "Excavator", pathAttr: "M16.997 …" },
  { name: "Car", pathAttr: "M23.5 …" },
  { name: "Bus", pathAttr: "M8.829 …" },
];		
const colorScale = d3.scaleOrdinal().range(d3.schemeSet1); // *) see below

const tooltip = d3.select('body')
  .append("span")
  .style("position", "absolute");		  

const svg = d3.select("#tooltipSVG");
svg.selectAll("path")
  .data(vehicles)
  .join("path")
  .attr("d", d => d.pathAttr)
  .style("fill", (d,i) => colorScale(i))
  .attr("transform", (d,i) => `translate(${i*35},0)`)
  .on('mouseover', function(e, d) {
    tooltip.text(d.name);
  })			
  .on("mousemove", function(e, d) {
    tooltip
      .style('left', e.pageX + 'px')
      .style('top', e.pageY - 30 + 'px');
  })
  .on("mouseout", function(e, d) {
    tooltip.text(" ");
  });

Result:

Hover over the figures:

SVG graphics from iconmonstr.

*): Scales will be covered later. The used color scale in this example is a Ordinal scale.

pageX and pageY return the x and y coordinates (in pixels) of the mouse position, relative to the top-left corner of the entire document. This includes any portion of the document not currently visible. d3.pointer represents the coordinates of the specified event relative to the specified target:


const display = d3.select("#display");		
d3.select("body")
  .insert("svg", "#display")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("viewBox", "0 0 200 50")
    .style("background", "tan")
    .style("cursor", "crosshair")
    .on("mousemove", function(e, d) {
      const [x, y] = d3.pointer(e); // (array destructuring)
      display.text(`${Math.round(x)}, ${Math.round(y)}`);
    })
    .on("mouseout", function(e, d) {
      display.text("Hover over the square...");
    });	

Result:

Hover over the square...