7 Interactivity

Read: IDVW2, Chapter 10 Interactivity

7.1 Binding event listeners to SVG elements

(100, 150)

It’s helpful to think carefully about what you want to happen when an event listener is triggered and what information you need. Open Developer Tools and try these in the Console. Note that event management changed in v6 so code written for earlier versions of D3 will not work.

7.1.1 Do something unrelated to the element that received the event

d3.select("svg")
  .on("click", function () {
    d3.select("svg")
      .append("text")
        .attr("x", "100")
        .attr("y", "40")
        .text("Hello World");
        });

7.1.2 Change an attribute of the element that received the event

d3.select("line")
  .on("click", function() {
    d3.select(this)
      .attr("stroke-width", "10");
      });

In the context of event handlers, “this” is the element that received the event, a.k.a. what you clicked on if it’s a click event. An alternative (\(\geq\) v6, see link above) is to pass the event and access the element with event.currentTarget:

or

d3.select("line")
  .on("click", function(event) {
    d3.select(event.currentTarget)
      .attr("stroke", "yellow");
      });

7.1.3 Get the value of an attribute of the element that received the event

d3.select("circle")
  .on("click", function(event) {
  const rad = d3.select(event.currentTarget).attr("r");
  d3.select("text")
    .text(`The radius is ${rad} pixels.`);
    });

7.1.4 Do something with the data bound to the element that received the event

d3.select("circle")
  .data([{s: "red", sw: "15"}])
  .on("click", function(event, d) {
    d3.select(event.currentTarget) 
      .attr("stroke", d.s)
      .attr("stroke-width", d.sw);
      });

Note that starting with v6, the data is the 2nd parameter to be passed: function(event, d). In addition, note that you do not need to pass d again when accessing the data: for example we use d.s not d => d.s.

As in the previous example, d3.select(this) can be used instead of d3.select(event.currentTarget).

Try changing the data value bound to the circle with d3.select("circle").datum("10") and clicking again.

7.1.5 Get the svg location of the event

d3.select("svg")
  .on("click", function(event) {
    d3.select("text")
      .text(`(${d3.pointer(event).map(Math.round)})`)
      });

(Up to v5, d3.mouse(this) was used instead of d3.pointer(event).)

7.2 Separating the function and event listener

Examples

function goyellow() {
  d3.select(this)
    .attr("fill", "yellow")
    };
d3.select("circle")
  .on("mouseover", goyellow);

7.3 HTML buttons

<button type="button" onclick="showdate()">Click for date</button>
function showdate() {
  console.log(Date());
}

7.3.1 Exercise

Add buttons as indicated to this file.

7.4 Radio buttons

HTML:

<p id="color" style="background-color: silver; color: white;">
   Please select your favorite primary color:</p>
<input type="radio" name="fav_color" value="red">red</input>
<input type="radio" name="fav_color" value="blue">blue</input>
<input type="radio" name="fav_color" value="yellow">yellow</input>

Note:

  • type is always radio for radio buttons
  • name is shared for a group of radio buttons
  • value is unique

JavaScript:

d3.selectAll('input[name="fav_color"]')
  .on("click", function(event) {
    var favcolor = event.currentTarget.value;
    d3.select("p#color").style("color", favcolor);
    });

Please select your favorite primary color:

red blue yellow

7.5 Dependent event listeners

In these examples, the behavior or existence of one event listener depends on another.

7.5.1 Global variable example

Here the circle click behavior depends on the value of the radio button: if the “Move left” radio button is checked, the circle will move left when clicked. If the “Move right” radio button is checked, the circle will move right when clicked.

A global variable is used to keep track of the radio button value. The event listener on the circle conditions the behavior on the value of this global variable.

Click the circle.

 Move left  Move right
svg#radio

// global variable keeps track of which radio button is clicked
let action = "left";
d3.select("div#rad")
  .selectAll("input")
  .on("click", function() { action = d3.select(this).node().value; });
      
// circle click behavior depends on value of "action"
d3.select("svg#radio").select("circle")
  .on("click", function () {
    let cx_new;
    if (action == "left") {
      cx_new = +d3.select(this).attr("cx") - 50;
      if (cx_new < 20) cx_new = 20;
      } else {
      cx_new = +d3.select(this).attr("cx") + 50;
      if (cx_new > 280) cx_new = 280;
      }
    d3.select(this)
      .transition()
      .duration(500)
      .attr("cx", cx_new);
      });

7.5.2 Turn off event listener

In this example, the event listeners on the squares are turned on or off depending on the value of the radio button. Event listeners can be removed by setting the behavior to null.

Click a square.

 Red active  Blue active

svg#radio2
// movement function
const jump = function () {
      d3.select(this).transition().duration(500)
      .attr('y', '0')
      .transition().duration(500).ease(d3.easeBounce)
      .attr('y', '75');
};

// initial setup: add event listener to red square
d3.select("svg#radio2")
  .select("rect#red")
  .on("click", jump);
    
// switch event listeners if radio button is clicked
d3.select("div#rad2").selectAll("input")
  .on("click", function () {
  if (d3.select(this).node().value == "blue") {
    d3.select("svg#radio2").select("rect#blue").on("click", jump);
    d3.select("svg#radio2").select("rect#red").on("click", null);
    } else {
    d3.select("svg#radio2").select("rect#red").on("click", jump);
    d3.select("svg#radio2").select("rect#blue").on("click", null);
    }
});