javascripthtmlhtml5-canvaswhiteboard

How to pass an event to html tag under canvas?


I'm trying to make a whiteboard in body tag with canvas but can't pass event to input tag under canvas.

Is it possible to pass a mouse click event to the input tag under canvas tag without change order of tag or z-index?

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      div {
        position: relative;
      }
      canvas {
        position: absolute;
        top: 0;
        left: 0;
        background: rgba(255, 0, 0, 0.5);
      }

      input {
        /* position: absolute;
        top: 100px;
        left: 60px; */
        color: #000;
        border: 1px solid #000;
      }
    </style>
    <script>
      window.onload = function () {
        var canvas = document.getElementById("canvas"),
          context = canvas.getContext("2d"),
          MARGIN = 35,
          RADIUS = canvas.width / 2 - MARGIN;
        context.beginPath();
        context.arc(
          canvas.width / 2,
          canvas.height / 2,
          RADIUS,
          0,
          Math.PI * 2,
          true
        );
        context.stroke();
      };
    </script>
  </head>
  <body>
    <div>
      <input type="text" placeholder="input tag" />
      <canvas id="canvas" width="500" height="500">
        Canvas not supported
      </canvas>
    </div>
  </body>
</html>
pencil function on whiteboard


Solution

  • You can set the CSS pointer-events property on the canvas to none.

    Original example:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style>
          div {
            position: relative;
          }
          canvas {
            position: absolute;
            top: 0;
            left: 0;
            background: rgba(255, 0, 0, 0.5);
            pointer-events: none;
          }
    
          input {
            /* position: absolute;
            top: 100px;
            left: 60px; */
            color: #000;
            border: 1px solid #000;
          }
        </style>
        <script>
          window.onload = function () {
            var canvas = document.getElementById("canvas"),
              context = canvas.getContext("2d"),
              MARGIN = 35,
              RADIUS = canvas.width / 2 - MARGIN;
            context.beginPath();
            context.arc(
              canvas.width / 2,
              canvas.height / 2,
              RADIUS,
              0,
              Math.PI * 2,
              true
            );
            context.stroke();
          };
        </script>
      </head>
      <body>
        <div>
          <input type="text" placeholder="input tag" />
          <canvas id="canvas" width="500" height="500">
            Canvas not supported
          </canvas>
        </div>
      </body>
    </html>

    Updated example

    The events that are related to the canvas (click, mousedown, mousemove ect.) are not related to or specific for the canvas element. If you can track the mouse or a pointer device over the same area, that is fine. So, here I have the event listeners on the parent <div> for the canvas. The <div> contains the canvas and has the same coordinate point in the upper left corner. So, the canvas can still have the property pointer-events set to none, and you can use the <div> for tracking the position of the mouse/pointer device.

    I changed the CSS position properties to a grid layout, where the input element and the canvas are placed in the same grid area, and therefore still overlapping. A kind of modern version of the position property...

    const canvas = document.getElementById("canvas"),
      context = canvas.getContext("2d"),
      container = document.getElementById("container");
    var MARGIN = 35,
      RADIUS = canvas.width / 2 - MARGIN;
    container.mousedown = false;
    context.beginPath();
    context.arc(
      canvas.width / 2,
      canvas.height / 2,
      RADIUS,
      0,
      Math.PI * 2,
      true
    );
    context.stroke();
    
    container.addEventListener('mousedown', e => {
      let container = e.target.closest('#container');
      container.mousedown = true;
    });
    
    document.addEventListener('mouseup', e => {
      container.mousedown = false;
    });
    
    container.addEventListener('mousemove', e => {
      let container = e.target.closest('#container');
      let canvasX = e.clientX - container.offsetLeft;
      let canvasY = e.clientY - container.offsetTop;
      if (container.mousedown) draw(canvasX, canvasY);
    });
    
    function draw(x, y) {
      context.beginPath();
      context.arc(x, y, 5, 0, Math.PI * 2, true);
      context.fill();
    }
    body {
      margin: 10px 0 0 50px;
    }
    
    div#container {
      display: grid;
    }
    
    canvas {
      background: rgba(255, 0, 0, 0.5);
      pointer-events: none;
      grid-column: 1;
      grid-row: 1;
    }
    
    input {
      grid-row: 1;
      grid-column: 1;
      place-self: start;
      color: #000;
      border: 1px solid #000;
    }
    <div id="container">
      <input type="text" placeholder="input tag" />
      <canvas id="canvas" width="500" height="500">
            Canvas not supported
      </canvas>
    </div>