最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Re-firing pointer events onto lower layer (for irregular-shaped dragging with interact.js) - Stack Overflow

programmeradmin0浏览0评论

I need to manually construct/fire a mousedown event in a way that can be handled by any relevant event handlers (either in straight JS, jQuery, or interact.js), just as a natural mousedown event would. However, the event does not seem to trigger anything the way I expect it to.

I am trying to make some irregularly shaped images draggable using the interact.js library. The images are simply rectangular img elements with transparent portions. On each element, I have defined 2 interact.js event listeners:

  1. Checking if the click was inside the image area, disabling drag if not (fires on "down" event)
  2. Handling the drag (fires on "drag" event)

However, if the img elements are overlapping, and the user clicks in the transparent area of the top element but on the filled area of the lower element, the lower element should be the target of the drag. After trying several things (see below), I have settled on the solution of: re-ordering the z-indexes of the elements at the same time that I disable drag in step 1, then re-firing the mousedown event on all lower elements. I'm using the "native" event type (not jQuery or interact.js) in hopes that it will literally just replicate the original mousedown event.

// code to re-assign "zIndex"s
function demote(element, interactible, event){

    // block dragging on element
    interactible.draggable(false);

    // get all images lower than the target 
    var z = element.css("zIndex");
    var images = $("img").filter(function() {
    return Number($(this).css("zIndex")) < z;
    });

    // push the target to the back
    element.css("zIndex",1);

    // re-process all lower events
    $(images).each( function () {
          // move element up
          $(this).css("zIndex",Number($(this).css("zIndex"))+1);

          // re-fire event if element began below current target
          elem = document.getElementById($(this).attr('id'));

          // Create the event.
          e = new MouseEvent("mousedown", {clientX: event.pageX, clientY: event.pageY});
          var cancelled = !elem.dispatchEvent(e);
    });
}

Unfortunately, this does not work, as the mousedown event does not register with any of the handlers. Why?

I have all the (relevant) code at this JSFiddle: / Note that this JSFiddle does not seem to work as smoothly as it does in a normal browser window, but I think it demonstrates the intended functionality.

Other things I have tried:

Lots of people have proposed different schemes to handle similar problems (i.e. forwarding events to lower layers, using pointer-events: none, etc), but none seem to work to trigger the interact.js handler and start a drag interaction on the right element. I also tried using interaction.start (provided by interact.js) but it seems buggy--there is at least one open issue on the topic and when I tried to start a new drag interaction on a target of my choice I got lots of errors from within the library's code.

I'm not against revisiting any of these solutions per se, but I would also really like to know why manually firing a mousedown event won't work.

I need to manually construct/fire a mousedown event in a way that can be handled by any relevant event handlers (either in straight JS, jQuery, or interact.js), just as a natural mousedown event would. However, the event does not seem to trigger anything the way I expect it to.

I am trying to make some irregularly shaped images draggable using the interact.js library. The images are simply rectangular img elements with transparent portions. On each element, I have defined 2 interact.js event listeners:

  1. Checking if the click was inside the image area, disabling drag if not (fires on "down" event)
  2. Handling the drag (fires on "drag" event)

However, if the img elements are overlapping, and the user clicks in the transparent area of the top element but on the filled area of the lower element, the lower element should be the target of the drag. After trying several things (see below), I have settled on the solution of: re-ordering the z-indexes of the elements at the same time that I disable drag in step 1, then re-firing the mousedown event on all lower elements. I'm using the "native" event type (not jQuery or interact.js) in hopes that it will literally just replicate the original mousedown event.

// code to re-assign "zIndex"s
function demote(element, interactible, event){

    // block dragging on element
    interactible.draggable(false);

    // get all images lower than the target 
    var z = element.css("zIndex");
    var images = $("img").filter(function() {
    return Number($(this).css("zIndex")) < z;
    });

    // push the target to the back
    element.css("zIndex",1);

    // re-process all lower events
    $(images).each( function () {
          // move element up
          $(this).css("zIndex",Number($(this).css("zIndex"))+1);

          // re-fire event if element began below current target
          elem = document.getElementById($(this).attr('id'));

          // Create the event.
          e = new MouseEvent("mousedown", {clientX: event.pageX, clientY: event.pageY});
          var cancelled = !elem.dispatchEvent(e);
    });
}

Unfortunately, this does not work, as the mousedown event does not register with any of the handlers. Why?

I have all the (relevant) code at this JSFiddle: https://jsfiddle/tfollo/xr27938a/10/ Note that this JSFiddle does not seem to work as smoothly as it does in a normal browser window, but I think it demonstrates the intended functionality.

Other things I have tried:

Lots of people have proposed different schemes to handle similar problems (i.e. forwarding events to lower layers, using pointer-events: none, etc), but none seem to work to trigger the interact.js handler and start a drag interaction on the right element. I also tried using interaction.start (provided by interact.js) but it seems buggy--there is at least one open issue on the topic and when I tried to start a new drag interaction on a target of my choice I got lots of errors from within the library's code.

I'm not against revisiting any of these solutions per se, but I would also really like to know why manually firing a mousedown event won't work.

Share Improve this question edited Apr 13, 2016 at 16:02 TylerH 21.1k79 gold badges79 silver badges114 bronze badges asked Mar 15, 2016 at 23:13 TimTim 4043 silver badges12 bronze badges 5
  • Which browser are you using? IE doesn't support constructing Event objects - you have to use static methods such as document.createEvent() or something. – TheHans255 Commented Apr 12, 2016 at 14:26
  • I was working in Chrome. I'd like to maximize patibility though, so I'll try to refactor to use createEvent(). – Tim Commented Apr 13, 2016 at 3:00
  • 2 MDN says that createEvent() is deprecated and that event constructors (which you are using) are the preferred method for creating event objects. – TheHans255 Commented Apr 13, 2016 at 4:24
  • It would be helpful for me to know whether performance is a critical aspect. I think in order to do this task you need to know if a given pixel at a given coordinate is visible or not. This operation however might be a bit expensive. – Tim Hallyburton Commented Apr 13, 2016 at 15:01
  • Performance is not too critical. I'm already reordering the z-index of every img element on every click and checking the pixel offset of every click, so I think solving this problem in a clean way would help reduce the waste that is happening currently. – Tim Commented Apr 13, 2016 at 15:10
Add a ment  | 

2 Answers 2

Reset to default 1

The idea is to listen down event on parent element and to choose manually drag target. Also I didn't use z-index for choosing which image to drag, because z-index doesn't work with position:static. Instead of that I just gave priorities to images, it's all up to you. https://jsfiddle/yevt/6wb5oxx3/3/

var dragTarget;

function dragMoveListener (event) {
  var target = event.target,
      // keep the dragged position in the data-x/data-y attributes
      x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
      y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

  // translate the element
  target.style.webkitTransform =
    target.style.transform =
    'translate(' + x + 'px, ' + y + 'px)';

  // update the posiion attributes
  target.setAttribute('data-x', x);
  target.setAttribute('data-y', y);
}

function setDragTarget(event) {
  //choose element to drag
  dragTarget = $('#parent').find('img')
    .filter(function(i, el) {
      var clickCandicateRect = el.getBoundingClientRect();
      return insideBoundingRect(event.x, event.y, clickCandicateRect);
    }).sort(byPriority).get(0);
}

function insideBoundingRect(x, y, rect) {
  return (rect.left <= x) && (rect.top <= y) && (rect.right >= x) && (rect.bottom >= y);
}

function byPriority (a, b) {
  return $(b).data('priority') - $(a).data('priority');
}

function startDrag(event) {
  var interaction = event.interaction;
  var target = dragTarget;

  if (!interaction.interacting()) {
    interaction.start({ name: 'drag' }, event.interactable, target);
  }
}

//Give priority as you wish
$('#face1').data('priority', 2);
$('#face2').data('priority', 1);

interact('#parent').draggable({
  //use manualStart to determine which element to drag
  manualStart: true,
  onmove: dragMoveListener,
  restrict: {
    restriction: "parent",
    endOnly: true,
    elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
  },
})
.on('down', setDragTarget)
.on('move', startDrag);

Have you tried to set the css property pointer-events: none on the higher levels (it could also be set via javascript)?

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论