Plot Pointer - x,y coordinates

I am using plot to create a chart. The configuration looks like this:

var plot = Plot.plot({
...
  y: {grid: true, stroke: "#898989", label: null, ticks: interval},
  x: {label: null },
  marks: [
...
    Plot.dot(result, Plot.pointerX({x: properties.xAxisField, y: properties.yAxisField, fill: "#6078ff", r: 8 }))
  ]
})

All is well. The chart is created as expected. The question I have is related to Plot.pointerX. Based upon the documentation here: Pointer transform | Observable Plot we can create a listener for the pointer event as stated:

The pointer transform emits an input event whenever the focused points changes, and sets the value of the plot element to the focused data. This allows you to use a plot as an Observable view (viewof), or to register an input event listener to react to pointing.

I implemented this listener as follows and it works as stated and e.target.value contains the data from the point as expected:

document.addEventListener("input", updateValue);

function updateValue(e) {
  console.log(e);
  console.log(e.target.value);
}

The question I have is: Can the mouse position (x,y coords) be obtained from this event such that I know the position of the “dot” that is being hovered when the event is fired? Much thanks in advance

(@Fil tagging you in the off chance you happen to be available).

No, the input event generated by Plot is not a mouse or pointer event, and so doesn’t have corresponding coordinates for the mouse or pointer position. More technically, it is an InputEvent, which is a UIEvent, but you need a MouseEvent if you want mouse or pointer coordinates. Plot won’t tell you the mouse event that triggered the input event.

That said, you can listen for mouse or pointer events yourself, and then do something with those coordinates or stash them somewhere, so that you know the mouse or pointer position(s) when you receive an input event from Plot.

Maybe you could describe what you are trying to do at a higher level?

@mbostock thanks for the quick response!

I’m basically trying to achieve the mouseover affect seen here: Monthly Recurring Revenue - Baremetrics Demo (but based upon plot rather then straight d3). Here is where I am at: Untitled / Brian | Observable (keeping in mind I’m using this outside of observable notebooks - standard js).

I want to overlay html on the SVG using standard JS but I need to know the location of the dot/pointer that is currently active so I can offset the location of the div appropriately. I can show/hide the div already, but the location is problematic. As a note, I’m using the standard tip currently just as a proof of concept, but that would go away once the JS/HTML solution is achievable. Any thoughts/insights are greatly appreciated.

And thanks as always for the awesome work you continue to do on d3/plot!

This may be of interest… using tippy.js with Plot to create HTML tooltips.

@Cobus Thank you! I did see that post and I agree that is the idea I am going for. However it appears the d3 selector is for all “circles”

  let dots = d3.select(map).selectAll("circle");

I actually have two different circles (one at the top of the area and one on the baseline), only one of which (the top) should be used for the position of the tooltip…

Screenshot 2023-07-26 at 4.35.39 PM

Is it possible to give a more specific selector to one of these so as to target one over the other?

    Plot.dot(result, Plot.pointerX({x: "x", y: 0, py: "y", fill: "#FFF", stroke: "#E5E7EB", strokeWidth:1, r: 5, strokeOpacity: 1, fillOpacity:1 })),        
    Plot.dot(result, Plot.pointerX({x: "x", y: "y", fill: "#6078ff", r: 8 })),   

I think it would be great if you could specify a class or id attribute to a mark but I don’t think that’s an option. Of course, you can take any action you like as a function of the title attribute. For example, you might take no action at all, if the title is null.

@mcmcclur I believe this would solve my present issue. Also, it seems you mentioned this at some point (here) in the past and there has been an enhancement coded already here: Add class name option for marks by RLesser · Pull Request #1098 · observablehq/plot · GitHub

@mbostock @Fil any chance this pull request could be merged into the main branch sometime in the near future?

Adding an id is problematic if you have several charts on a page, so this is probably not going to be done. I’m still in favor of a simple option to add a className (note that it’s already possible to do this within a render transform, but that’s obviously too complicated).

@Fil I’m specifically addressing the className (which is what would work for me) which is what I believe the patch I was asking about already solves?

Could you look into the pull request and see what is necessary to pull that into the main branch? I don’t think it has anything to do with “id”'s based on what I read, but I could be wrong?

Also, do you have an example of the render transform? I’m willing to use that for the moment if it can target one set of plot “circles/dots” and not the other.

I know, I was commenting on Mark’s mention of id. For an example with the render transform, see opacity artifacts in line segments - #2 by Fil. We don’t recommend using that route though, because it’s much more complicated that needs to be.

In your case, you could set a specific ariaDescription option on the mark you want to target, for example ariaDescription: "informational dots about cities".
Then select this marks’ dots with, for example:

d3.select(map).selectAll(“[aria-description^=informational] circle”);

In a sense this works like the desired className, except it’s “hijacking” the aria-description property—and as a consequence it’s recommended to set the contents of that property to a semantically correct sentence.

2 Likes

@Fil thank you, I like that idea! I’m going to give this a go and see if I can get it working with that feedback!

This works as described. Thank you @Fil!