Home > database >  JavaScript event - mouseover attached to SVG rectangles, partially failed to response
JavaScript event - mouseover attached to SVG rectangles, partially failed to response

Time:08-13

The following is using create-react-app created the boilerplate and rendered the only component is TestComponent. In TestComponent it rendered a SVG showing a few rectangles. (svg rectangles

not working on red console log

To ensure there is no overlay among these rectangles, I have removed all the horizontal rectangles. And the mouseover events are the same things, the third and the fourth vertical rectangles mouseover events not working.

enter image description here

I am keen to believe this is a brower bug but I am not sure. The reason is if I change the stoke-width to 20px, the third and fourth rectangle responsed to the mouseover event. However, if the strike-width is 1px, no matter how many times I move across these two rectangles, it has no response at all.

I am using the latest Chrome browser - Version 104.0.5112.81 (Official Build) (64-bit).

enter image description here

index.js

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import TestComponent from "./components/test.jsx";
import reportWebVitals from "./reportWebVitals";

const root = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <TestComponent />
  </React.StrictMode>,
  root
);
reportWebVitals();

test.jsx

import React, { Component } from "react";

export default class TestComponent extends Component {
  onRectMouseOver = (e) => {
    console.log(`${e.clientX} ${e.clientY}`);
  };
  render() {
    const rects = JSON.parse(
      "[[0, 45, 45, 2370], [1955, 45, 45, 2370], [355, 45, 45, 2370], [710, 45, 45, 2370], [1065, 45, 45, 2370], [1420, 45, 45, 2370], [1775, 45, 45, 2370], [45, 845, 310.0, 45], [45, 1645, 310.0, 45], [400, 845, 310.0, 45], [400, 1645, 310.0, 45], [755, 845, 310.0, 45], [755, 1645, 310.0, 45], [1110, 845, 310.0, 45], [1110, 1645, 310.0, 45], [1465, 845, 310.0, 45], [1465, 1645, 310.0, 45], [1820, 845, 135.0, 45], [1820, 1645, 135.0, 45], [0, 2415, 2000, 45], [0, 0, 2000, 45]]"
    );

    return (
      <div style={{ width: 5000, height: 3000, backgroundColor: "grey" }}>
        <svg
          viewBox="0 0 5000 3000"
          xmlns="http://www.w3.org/2000/svg"
          transform="scale(1,-1)"
        >
          <g>
            {rects &&
              rects.map((rect, index) => {
                let [x, y, width, height] = rect;

                return (
                  <rect
                    x={x}
                    y={y}
                    width={width}
                    height={height}
                    key={index}
                    style={{ fill: "None", strokeWidth: 1, stroke: "black" }}
                    onm ouseOver={(e) => {
                      this.onRectMouseOver(e);
                    }}
                  >
                    <title>
                      x: {x}; y: {x}; width: {width}; height:
                      {height}
                    </title>
                  </rect>
                );
              })}
          </g>
        </svg>
      </div>
    );
  }
}

CodePudding user response:

I am keen to believe this is a Chrome broswer bug.

It is working on Firefox - 103.0.2 (64-bit).

CodePudding user response:

Since you were using fill: none (and the default value of pointer-events property), only the borders will ever trigger an event, and not the interior. And each of your rectangles has very narrow strokes, only 1px wide (might occupy 2px because of "1px border blur"*), so if you move very fast, it's possible to skip more than just 3rd and 4th rectangles (in fact it's not that hard to skip most of the rectangles).

You can see how easy it is to miss the rectangles, e.g. try to use right-click and inspect, or use "Select an element (Ctrl Shift C)" of in the Chrome DevTools.

I can reproduce the effect on Firefox as well, by moving the mouse very fast (not limited to 3rd or 4th rectangles).

(*by "1px border blur", I mean, e.g. the 3B of https://vecta.io/blog/5-most-common-problems-faced-by-svg-users 3B. And there is a similar problem with canvas: https://usefulangle.com/post/17/html5-canvas-drawing-1px-crisp-straight-lines )

Also, just using Chrome, I managed to trigger your third and the fourth vertical rectangles simply by using super-slow horizontal movements of the mouse. So, I guess the actual problem is that your mouse pointer can move more than 2px per unit of time, so it's easy to "jump" over the border without triggering anything. I think the browsers worked as they should.

Work around 1: One possible work around is by using fill: transparent (which is the same as fill: rgba(0,0,0,0)), so the interior can trigger mouse events. Then it's not that easy to miss a rectangle. (There may be side effects, though, since the sensitive areas are larger now).
[edit: one side effect is that the rendering is more expensive, as it would have to paint over the interior, and then the "opacity" means that you calculate the result by combining the colors of current object and the colors of the background, even though it has no visible effect due to 0 opacity (i.e. output is the same as just background)]

Work around 2: Change pointer-events css property.
Here pointer-events: fill works even though the fill is none
(I used to wonder if it was intentional, as I thought with fill: none there should be not any 'fill' to trigger any pointer event. But I found from the description in https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events that it's intentional)
And it works both with Chrome and Firefox.
You may also change it to pointer-events: visible if you also want the border to trigger events (instead of just the interior), but then there is not much difference when your borders are very thin. (There is also pointer-events: all but then it may be surprising when visibility is hidden)

  • Related