Chidi
Chidi

Reputation: 35

How to pass click event on html5 canvas to event listener on sibling canvas element

I am building a board game application, and the actual board game plays on a html5 canvas element with multiple layers. To be specific, there are multiple canvas elements in a div that occupy the same position and are the same size, but are layered using the z-index property. Code:

<div style="position: relative">
      <canvas
        id="base_layer"
        style="position: absolute; left: 0; top: 0; z-index: 0"
      ></canvas>
      <canvas
        id="go_layer"
        style="position: absolute; left: 0; top: 0; z-index: 1"
      ></canvas>
      <canvas
        id="chess_layer"
        style="position: absolute; left: 0; top: 0; z-index: 2"
      ></canvas>
    </div>

The way it's set up, any click on the canvas is only registered to the chess layer. I want to pass click events on the chess layer to the go layer, but I don't know how to do that exactly. I have tried using jQuery to trigger a click event on the go layer anytime someone clicks on the chess layer, but that click event does not have the position information that the original click does. Original click:

MouseEvent {isTrusted: true, screenX: 452, screenY: 228, clientX: 452, clientY: 157, …}
altKey: false
bubbles: true
button: 0
buttons: 0
cancelBubble: false
cancelable: true
clientX: 452
clientY: 157
composed: true
ctrlKey: false
currentTarget: null
defaultPrevented: false
detail: 1
eventPhase: 0
fromElement: null
isTrusted: true
layerX: 444
layerY: 149
metaKey: false
movementX: 0
movementY: 0
offsetX: 445
offsetY: 149
pageX: 452
pageY: 157
path: (6) [canvas#chess_layer, div, body, html, document, Window]
relatedTarget: null
returnValue: true
screenX: 452
screenY: 228
shiftKey: false
sourceCapabilities: InputDeviceCapabilities {firesTouchEvents: false}
srcElement: canvas#chess_layer
target: canvas#chess_layer
timeStamp: 2274.085000004561
toElement: canvas#chess_layer
type: "click"
view: Window {window: Window, self: Window, document: document, name: "", location: Location, …}
which: 1
x: 452
y: 157
__proto__: MouseEvent

Triggered click on sibling layer:

MouseEvent {isTrusted: false, screenX: 0, screenY: 0, clientX: 0, clientY: 0, …}
altKey: false
bubbles: true
button: 0
buttons: 0
cancelBubble: false
cancelable: true
clientX: 0
clientY: 0
composed: true
ctrlKey: false
currentTarget: null
defaultPrevented: false
detail: 0
eventPhase: 0
fromElement: null
isTrusted: false
layerX: -8
layerY: -8
metaKey: false
movementX: 0
movementY: 0
offsetX: 0
offsetY: 0
pageX: 0
pageY: 0
path: (6) [canvas#go_layer, div, body, html, document, Window]
relatedTarget: null
returnValue: true
screenX: 0
screenY: 0
shiftKey: false
sourceCapabilities: null
srcElement: canvas#go_layer
target: canvas#go_layer
timeStamp: 2277.075000005425
toElement: canvas#go_layer
type: "click"
view: Window {window: Window, self: Window, document: document, name: "", location: Location, …}
which: 1
x: 0
y: 0
__proto__: MouseEvent

Notice how, for the triggered click, event.clientX = event.clientY = 0, which is not the same as the original click. How do I pass the original click event completely to the other layer, without losing information? The code I am using to trigger the click event in the go layer is (this occurs within the click event listener for the chess layer):

// Since this is the top layer, pass click event to go_layer
    var gevent = jQuery.Event("click");
    gevent.clientX = event.clientX;
    gevent.clientY = event.clientY;
    $("canvas#go_layer").trigger(gevent);
    console.log(event);

Upvotes: 1

Views: 873

Answers (1)

obscure
obscure

Reputation: 12891

I can't tell you why your JQuery approach fails. Nevertheless, you can do the exact same thing using plain JavaScript, with the exception that it'll actually work.

Basically let event=document.createEvent('Event'); is used to create an event. Since it's a click event you're after, we need to call

event.initEvent('click', true, true);

on the newly created event. As with your JQuery approach this new event isn't a clone of the original so we need to manually copy all of the properties we're interested in.

event.clientX = e.clientX;
event.clientY = e.clientY;

Afterwards just trigger the event on the desired object using dispatchEvent() e.g.

document.getElementById("go_layer").dispatchEvent(event);

Here's an example:

function canvasClicked(e) {
  if (e.target.id == "chess_layer") {
    let event = document.createEvent('Event');
    event.initEvent('click', true, true);
    event.clientX = e.clientX;
    event.clientY = e.clientY;
    document.getElementById("go_layer").dispatchEvent(event);
  }

  console.log(e.target.id, e.clientX, e.clientY);
}
document.getElementById("chess_layer").addEventListener("click", canvasClicked);
document.getElementById("go_layer").addEventListener("click", canvasClicked);
canvas {
  border: 1px solid black;
}
<div style="position: relative">
  <canvas id="base_layer" style="position: absolute; left: 0; top: 0; z-index: 0"></canvas>
  <canvas id="go_layer" style="position: absolute; left: 0; top: 0; z-index: 1"></canvas>
  <canvas id="chess_layer" style="position: absolute; left: 0; top: 0; z-index: 2"></canvas>
</div>

Upvotes: 1

Related Questions