Accepted answer

In your zoomed function, check to see if d3.event.sourceEvent is defined. A zoom triggered by,someZoomTransform) has a sourceEvent of null. Also, sourceEvent.type can provide additional information on the type of event (eg: wheel, mousemove).

This is the approach used in Bostock's brush and zoom example. Manually brushing triggers a programmatic update of the zoom and manually zooming triggers a programmatic update of the brush: the example needs to detect what is a manual vs programmatic zoom/brush to avoid an infinite loop.

Below a programmatic zoom is applied to the svg (covers the snippet preview window), but you can also pan/zoom. By checking to see if sourceEvent is null, we can see if a zoom was initiated by the user or programmatically:

var svg ="body")
  .attr("width", 600)
  .attr("height", 500);

var zoom = d3.zoom()
  .on("zoom", function() {
      if(d3.event.sourceEvent) {
        console.log("not programmatic zoom");
      else {
        console.log("programmatic zoom");
<script src=""></script>


You can check with this condition

 const action = d3.event.type === "zoom" && d3.event?.sourceEvent?.type === "mousemove" ? "PAN": "ZOOM";

Related Query

More Query from same tag