How to highlight a line when mouse over it in observable plot using JavaScript?

734 Views Asked by At

My chart looks like so::

Plot line chart

and here is my code:

linePlot = Plot.plot({
  marginLeft: 60,   // space to the left of the chart
  y: {
    type: "log", // set the type
  },
  marks: [
    Plot.line(data, {x: "timestamp", y: "views", z:"artist", title: d=>`${d.artist}`,})
  ]
})

I want to highlight or change color of each line when the mouse is over it.

3

There are 3 best solutions below

0
Mark McClure On

The easiest thing to do would be to attach a pointerenter event to the lines. Since you're using Observable, to use D3 to handle that process. Here's what it looks like on Observable:

https://observablehq.com/d/2e1daf099a7aaaea

To be clear, you are using two libraries: D3 and Plot, both of which are automatically available on Observable. You can use them both in vanilla Javascript pretty easily, though:

// Manufacture some data
let pt_lists = d3.range(10).map(() => {
  let cur = 0;
  return d3.range(1000).map(function(x) {
    let step = 2 * d3.randomInt(0, 2)() - 1;
    cur = cur + step;
    return [x, cur];
  });
});

// Plot the data
let plot = Plot.plot({
  marks: pt_lists.map((pts) => Plot.line(pts, {
    strokeWidth: 2
  }))
});

// Here's where the action is.
// We use d3 to select all the paths in the plot
d3.select(plot)
  .selectAll("path")
  // React when the pointer hovers over the path.
  .on("pointerenter", function() {
    d3.select(plot).selectAll("path").attr("opacity", 0.2);
    d3.select(this).attr("opacity", 1);
  });
// Reset the appearance when the pointer leaves the SVG
d3.select(plot).on("pointerleave", function() {
  d3.select(plot).selectAll("path").attr("opacity", 1);
});

// Attach the plot to the container DIV
d3.select('#chart').append(() => plot)
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script src="https://cdn.jsdelivr.net/npm/@observablehq/[email protected]"></script>

<div id="chart" style="width: 600px; height: 400px"></div>

0
Fil On

It might also be possible to do the interaction in css:

d3.select(chart)
  .append("svg:style")
  .text(`
    path:hover {stroke-width: 2px;}
  `)
0
Tibor Udvari On

Here is a solution that isn't fast, doesn't scale well, but is more readable for me.

import { goog, ibm } from "@observablehq/plot-test-data"
mutable selectedValue = {}
plot = {
  return Plot.plot({
    marks: [
      Plot.ruleY([0]),
      Plot.lineY(ibm.data, {x: "Date", y: "Close", stroke: d => ibm.data.includes(selectedValue) ? "red": "black"}),
      Plot.lineY(goog.data, {x: "Date", y: "Close", stroke: d => goog.data.includes(selectedValue) ? "red": "black"}),

      Plot.lineY(ibm.data, Plot.pointer({x: "Date", y: "Close", strokeWidth: 10, stroke: "red"})),
      Plot.lineY(goog.data, Plot.pointer({x: "Date", y: "Close", strokeWidth: 10, stroke: "red"})),
    ]
  });
}
{
  plot.addEventListener("input", (event) => {
    mutable selectedValue = plot.value;  
  });
}

To better understand how it works see the mutable and pointer documentation. Finally, here is a working example in observable.