AngularJS中的DragOver和DragLeave

时间:2017-04-07 14:39:47

标签: javascript jquery angularjs

我尝试创建一个元素,用户可以将鼠标悬停在拖动元素的同时慢慢向下滚动页面。这对于支持我的Drag-& -Drop编辑器是必要的。

我检测用户是否悬停在锚点上并且正在拖动,如果是,我启动$interval我开始慢速滚动。如果鼠标离开锚点,或者用户停止拖动,则应取消慢速滚动,但不是。以下是我的代码。

elem.on('dragover', mouseOver);

elem.on('dragleave', mouseLeave);
elem.on('dragend', mouseLeave);

var scroller = null;

function mouseOver(ev) {
    ev.preventDefault();

    console.log('over');

    scroller = $interval(function () {
        if (!scrolledToBottom()) {
            $(window).scrollTop($(window).scrollTop() + 1);
        }
    }, 10);

}

function mouseLeave(ev) {
    ev.preventDefault();

    console.log('left');

    $interval.cancel(scroller);
    scroller = null;
}

function scrolledToBottom() {
    return ($(window).scrollTop() + $(window).height()) == 
    $(document).height();
}

即使console.log发射得很好,滚动条也永远不会停止滚动。我认为这是因为Angular无法在事件内部看到它被触发并且无法将更改应用于滚动条。但是,Angular没有ngDragOver,我只能想到JQuery的实现。

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

您的代码存在的问题是您注册了类似的事件 elem.on('dragover', mouseOver)elem.on('dragleave', mouseLeave)elem.on('dragend', mouseLeave)以及During the operations, several event types are fired and some event types might be fired many times (for example the drag and dragover event types).

doc mentions

例如,您的dragover事件处理程序如下所示:

function mouseOver(ev) {
    ev.preventDefault();

    console.log('over');

    scroller = $interval(function () {
        if (!scrolledToBottom()) {
            $(window).scrollTop($(window).scrollTop() + 1);
        }
    }, 10);

}

因此,上述事件处理程序将多次触发$interval多次注释,并将松散对旧承诺的引用。

使用以下检查修复此问题:

var scroller  = null;
function mouseOver(ev) {
    ev.preventDefault();

    console.log('over');

    if (scroller === null) {  // <-- This check will prevent multiple $interval registration.
        scroller = $interval(function () {
            if (!scrolledToBottom()) {
                $(window).scrollTop($(window).scrollTop() + 1);
            }
        }, 10);
    }

}

我会删除elem.on('dragleave', mouseLeave)事件处理程序注册调用作为此事件处理程序,如果在启动元素拖动时也会调用注册。从而取消了自定义scoll逻辑。

此外,我建议更改您的dragend事件处理程序,以便在取消$interval之前进行检查,例如(这仅用于代码清晰度):

function mouseLeave(ev) {
    ev.preventDefault();

    if (scroller !== null) {  // <-- Make a check before cancelling the 
        $interval.cancel(scroller);
        scroller = null;
    }

}

这是一个尝试的实例:

&#13;
&#13;
angular.module('myApp', [])
  .directive('drag', function($interval) {
    return {
      restrict: 'A',
      link: function(scope, elem, attrs, ctrl) {

        elem.on('dragover', mouseOver);
        elem.on('dragend', mouseLeave);

        var scroller = null;

        function mouseOver(ev) {
          ev.preventDefault();

          if (scroller === null) {
            scroller = $interval(function() {
              if (!scrolledToBottom()) {
                $(window).scrollTop($(window).scrollTop() + 1);
              }
            }, 10);
          }

        }

        function mouseLeave(ev) {
          ev.preventDefault();

          if (scroller !== null) {
            $interval.cancel(scroller);
            scroller = null;
          }

        }

        function scrolledToBottom() {
          return ($(window).scrollTop() + $(window).height()) ==
            $(document).height();
        }
      }
    }

  });
&#13;
.container {
  padding: 0;
  margin: 0;
  background: blue;
  height: 800px;
}

.container .drag {
  height: 300px;
}

.container .drag a {
  color: white;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js "></script>

<div ng-app="myApp">

  <div class="container">
    
    <div class="drag">
      <a href="" drag>Drag Me</a>
    </div>

  </div>

</div>
&#13;
&#13;
&#13;