Sir
Sir

Reputation: 8280

Event listener on a canvas behind another element

I have a mouse move event listener attached to my canvas. But on top of my canvas is a div with a higher z-index, this contains some menu buttons.

The problem i face is, i want the mouse move event to still activate when the mouse is over the menu element - of which is over the canvas element.

Currently, it acts as if the mouse is no longer on top of the canvas because the menu element takes precedence due to z-index order.

Is there a way to make this event listener ignore any elements that get in the way?

My code:

var canvas = new function(){
      var self = this

      self.outputMouseData = function(evt,el){

        var pos = mouse.relativePosition(evt,el);

        el.ctx.clearRect(0,0,el.width,el.height);

        el.ctx.fillStyle    = "white";
        el.ctx.font         = "bold 16px Arial";
        el.ctx.fillText(pos.x+' | '+pos.y, el.width-150,el.height-10);  

      }
}

   elements.addEvent(foreground,'mousemove',
                     function(evt){ canvas.outputMouseData(evt,foreground); }
                     );

My HTML

<div class="parent"> 
   <canvas id="canvas"></canvas>
   <div class="menu">Menu output</div>
</div>

Parent is relative positioned. Canvas and menu are absolute positioned but menu is z-index'd on top of canvas.

Upvotes: 2

Views: 5246

Answers (4)

Max888
Max888

Reputation: 3760

Rather than having to use a wrapper as suggested in other answers, you can simply apply pointer-events: none; to the menu div. This will allow the canvas to emit a mousemove event.

.menu {
  pointer-events: none;
}

Upvotes: 1

Amit
Amit

Reputation: 46323

HTML Events are bubbling up the node tree (You can read a good explanation about that here).

What that means is that if you attach an event handler to an element that contains other elements, that handler is called even when the event occurs on a child element (given bubbling wasn't explicitly aborted). You can wrap your canvas & div in a wrapper element (a span for example) and attach the handler on that. You'll get the event regardless of z-index.

Here's a short example of what the code could look like (the getOffsetSum of taken from here):

var canvas = document.getElementById('canvas');
var canvasPos = getOffsetSum(canvas);
var ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(0, 0, 99, 99);

var container = document.getElementById('container');
var mousemove = document.getElementById('mousemove');
container.addEventListener('mousemove', function(evt) {
  var pos = getOffsetSum(evt.target);
  pos.top += evt.offsetY - canvasPos.top;
  pos.left += evt.offsetX - canvasPos.left;
  mousemove.innerHTML = 'offsetX: ' + evt.offsetX + ' | offsetY: ' + evt.offsetY + '<br/>' +
    'canvasX: ' + pos.left + ' | canvasY: ' + pos.top;
});

function getOffsetSum(elem) {
  var top = 0, left = 0;

  while (elem) {
    top = top + parseInt(elem.offsetTop);
    left = left + parseInt(elem.offsetLeft);
    elem = elem.offsetParent;
  }

  return {
    top: top,
    left: left
  }
}
.ontop {
  position: absolute;
  top: 20px;
  left: 20px;
}
<span id="container">
  <canvas id="canvas" width="100" height="100"></canvas>
  <div class="ontop">ON TOP</div>
</span>
<div id="mousemove"></div>

Upvotes: 4

user3329460
user3329460

Reputation: 49

My initial thoughts was to add the "mousemove" event to the document itself (instead of the canvas element or elements), and track the elements using evt.currTarget or evt.target. by this way mousemove evt will always be present whether you hover over the menu or canvas (or anywhere for that matter).

Upvotes: 0

markE
markE

Reputation: 105015

Put both the canvas and your menu-div into a container div.

Then use jQuery event delegation to listen for mousemove events on the container but have your canvas respond to those mousemove events.

$('#container').on('mousemove','#myCanvas',function(){ doSomething(); }

You can learn more about event delegation here: http://api.jquery.com/on/

Upvotes: 2

Related Questions