1

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.

Sir
  • 8,135
  • 17
  • 83
  • 146
  • if the overlay is a child element, you can use addeventlistener with true as the third parameter, it should activate the parent event – David Aug 06 '15 at 22:36
  • Canvas' don't have child elements. The menu is positioned absolute inside a parent which is relative. – Sir Aug 06 '15 at 22:40
  • - sry this was meant to append marks answer..nway i see you have a solution! – David Aug 06 '15 at 23:20

4 Answers4

4

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>
Community
  • 1
  • 1
Amit
  • 45,440
  • 9
  • 78
  • 110
  • But will i be able to get mouse position relative to the canvas doing it that way? What if the canvas was offset inside the parent div - it would complicate it a bit surely? – Sir Aug 06 '15 at 22:34
  • Maybe, but since you'll be generating a wrapper div who's sole purpose is to receive events, you can make it the exact same size & position. Also, it's not that terrible to adjust X/Y coordinates to an element. – Amit Aug 06 '15 at 22:37
  • Thank you for the example, shame this problem in javascript doesn't have an elegant solution to it built into the language - but as long as this works its good! – Sir Aug 06 '15 at 23:11
  • This is not a javascript issue at all. It's a DOM "issue". anyway, glad I could help – Amit Aug 06 '15 at 23:12
2

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/

markE
  • 102,905
  • 11
  • 164
  • 176
  • This is useful but i'm not using jquery. I use vanilla JS. – Sir Aug 06 '15 at 22:35
  • You can accomplish event delegation with vanilla JS. It's just more complicated because you have to manually manage the event bubbling. Personally, I use jQuery to simplify delegation, so you might ask @Amit to lead you through the vanilla version. Good luck with your project! – markE Aug 06 '15 at 22:37
1

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;
}
Max888
  • 3,089
  • 24
  • 55
0

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).

  • But this will activate the event even if i move the mouse when not over the canvas. – Sir Aug 06 '15 at 22:34
  • Exactly, i think this is what you expected right? you will always get the mousemove evt when you are over the canvas OR when you are over the menu element. You will need to invoke your methods only if the mouse event.target is the canvas or the menu element. Else, you can add a parent div which covers both the menu and canvas and add the mousemove event for the parent div. – user3329460 Aug 06 '15 at 22:58
  • No i only wanted it to activate when the mouse is in the bounds of the canvas not the whole window. – Sir Aug 06 '15 at 23:09