使用AngularJS处理相同元素的ng-click和ng-dblclick

时间:2013-12-07 17:38:46

标签: javascript angularjs angularjs-directive angularjs-ng-click

我一直在寻找AngularJS的单击和双击事件处理,因为即使为我们的元素设置了ng-dblclick指令,AngularJS也只会触发ng-click事件。

以下是寻求解决方案的人的一些工作代码:

JS:

function MyController($scope) {

    var waitingForSecondClick = false;
    $scope.singleClickAction = function() {

        executingDoubleClick = false;
        if (waitingForSecondClick) {
            waitingForSecondClick = false;
            executingDoubleClick = true;
            return $scope.doubleClickAction();
        }
        waitingForSecondClick = true;

        setTimeout(function() {
            waitingForSecondClick = false;
            return singleClickOnlyAction();
        }, 250); // delay

        /*
         * Code executed with single AND double-click goes here.
         * ...
         */

        var singleClickOnlyAction = function() {
            if (executingDoubleClick) return;

            /*
             * Code executed ONLY with single-click goes here.
             * ...
             */

        }

    }

    $scope.doubleClickAction = function() {

        /*
         * Code executed ONLY with double-click goes here.
         * ...
         */

    };

}

HTML:

<div ng-controller="MyController">
    <a href="#" ng-click="singleClickAction()">CLICK</a>
</div>

所以我的问题是(因为我是一名AngularJS新手):有人可以更有经验地写一些很好的指令来处理这两件事吗?

在我看来,完美的方法是改变ng-click,ng-dblclick的行为,并添加一些“ng-sglclick”指令来处理单击式代码。 不知道它是否可能,但我发现它对每个人都非常有用。

随意分享您的意见!

6 个答案:

答案 0 :(得分:28)

你可以写自己的。我看一下角度处理点击的方式,并使用我在此处找到的代码对其进行修改:Jquery bind double click and single click separately

<div sglclick="singleClick()" ng-dblClick="doubleClick()" style="height:200px;width:200px;background-color:black">


mainMod.controller('AppCntrl', ['$scope', function ($scope) {
    $scope.singleClick = function() {
      alert('Single Click');
    }
    $scope.doubleClick = function() {
      alert('Double Click');
    }
}])


mainMod.directive('sglclick', ['$parse', function($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attr) {
          var fn = $parse(attr['sglclick']);
          var delay = 300, clicks = 0, timer = null;
          element.on('click', function (event) {
            clicks++;  //count clicks
            if(clicks === 1) {
              timer = setTimeout(function() {
                scope.$apply(function () {
                    fn(scope, { $event: event });
                }); 
                clicks = 0;             //after action performed, reset counter
              }, delay);
              } else {
                clearTimeout(timer);    //prevent single-click action
                clicks = 0;             //after action performed, reset counter
              }
          });
        }
    };
}])

这是一个例子

Plunker

答案 1 :(得分:24)

格雷格的答案肯定最接近最干净的答案。我将根据他的答案建立一个不需要编写新代码的版本,并且不需要在控制器中使用新的注入。

首先要问的是为什么超时会被用来解决这些问题。本质上,它们用于使函数跳过当前事件循环的其余部分,以便执行是干净的。但是,在角度中,您实际上对摘要循环的工作原理感兴趣。它与您的经典事件处理程序几乎相同,除了一些微小的差异,这使得它非常适合用户体验。您手头上处理函数执行顺序的一些工具包括scope.$evalscope.$evalAsyncscope.$applyscope.$applyAsync

我相信$apply会启动另一个摘要循环,离开$eval s。 $eval将使用当前$scope的上下文立即运行您包含的任何代码,$evalAsync会将您的函数排入队列,以便在摘要循环结束时运行。从本质上讲,$evalAsync$timeout的一个更清晰的版本,有一个很大的区别 - 第一个有上下文并存在于范围内!

这意味着您实际上可以在同一元素上处理ng-clickng-dblclick但请注意,这仍会在双击功能之前触发单击功能。这应该足够了:

<div ng-controller="MyController">
    <a href="#"
       ng-click="$evalAsync(singleClickAction())"
       ng-dblclick="doubleClickAction()">
       CLICK
    </a>
</div>

Here's a jsfiddle with the intended functionality using Angular 1.6.4

答案 2 :(得分:13)

遇到这个并且以为我会抛弃另一种选择。除了两个关键点之外,它与原始海报没有什么不同。

1)没有嵌套的函数声明。

2)我使用$ timeout。我经常使用$ timeout,即使没有延迟......特别是如果我开始承诺做其他工作。当摘要周期到来时,$ timeout将触发,这将确保应用范围内的任何数据更改。

鉴于

<img src="myImage.jpg" ng-click="singleClick()" ng-dblclick="doubleClick()">

在您的控制器中,singleClick功能如下所示:

$scope.singleClick = function () {
    if ($scope.clicked) {
        $scope.cancelClick = true;
        return;
    }

    $scope.clicked = true;

    $timeout(function () {
        if ($scope.cancelClick) {
            $scope.cancelClick = false;
            $scope.clicked = false;
            return;
        }

        //do something with your single click here

        //clean up
        $scope.cancelClick = false;
        $scope.clicked = false;
    }, 500);
};

doubleClick函数看起来很正常:

$scope.doubleClick = function () {

    $timeout(function () {

        //do something with your double click here

    });
};

希望这有助于某人...

答案 3 :(得分:2)

我试图找到一种处理双击并同时点击的方法。我使用这里的概念来取消原始点击。如果在延迟之前发生第二次单击,则执行双击操作。如果没有第二次点击,一旦延迟结束,默认的ngClick操作就会运行,并且会在元素上触发原始事件(并允许它像最初那样冒泡)。

实施例

<div ng-click="singleClick()"><span double-click="doubleClick()">double click me</span></div>

代码

.directive('doubleClick', function($timeout, _) {

  var CLICK_DELAY = 300
  var $ = angular.element

  return {
    priority: 1, // run before event directives
    restrict: 'A',
    link: function(scope, element, attrs) {
      var clickCount = 0
      var clickTimeout

      function doubleClick(e) {
        e.preventDefault()
        e.stopImmediatePropagation()
        $timeout.cancel(clickTimeout)
        clickCount = 0
        scope.$apply(function() { scope.$eval(attrs.doubleClick) })
      }

      function singleClick(clonedEvent) {
        clickCount = 0
        if (attrs.ngClick) scope.$apply(function() { scope.$eval(attrs.ngClick) })
        if (clonedEvent) element.trigger(clonedEvent)
      }

      function delaySingleClick(e) {
        var clonedEvent = $.Event('click', e)
        clonedEvent._delayedSingleClick = true
        e.preventDefault()
        e.stopImmediatePropagation()
        clickTimeout = $timeout(singleClick.bind(null, clonedEvent), CLICK_DELAY)
      }

      element.bind('click', function(e) {
        if (e._delayedSingleClick) return
        if (clickCount++) doubleClick(e)
        else delaySingleClick(e)
      })

    }
  }

})

答案 4 :(得分:1)

在这里加入答案:

  • 为简单起见使用@GregGrater strategy
  • 创建directive,作为@Rob(在此主题中被接受为最佳答案的那个)
  • 通过使用@EricChen answer
  • 替换ngClick内置指令来解决@Rob回答问题

此处 Plunker 具有该想法的本质(与此答案中的代码段相同;请参阅下文)。

除了注意:理想情况下,如果没有为元素定义ng-dblclick,则不应该阻止单击(此处为 Plunker fork 实施这个想法)

&#13;
&#13;
(function(angular) {
  'use strict';
var myApp = angular.module('myApp', []);

myApp.controller('myCtrl', ['$scope', function($scope) {
  $scope.click = false;
  $scope.singleClick = function() {
    $scope.click = 'single';
  };
  $scope.doubleClick = function() {
    $scope.click = 'double';
 };
}]);

// remove the buildin ng-Click, to solve issue with https://stackoverflow.com/a/20445344/4352306
myApp.config(function($provide) { // Source: https://stackoverflow.com/a/23209542/4352306
  $provide.decorator('ngClickDirective', ['$delegate', function ($delegate) {
   //$delegate is array of all ng-click directive, in this case 
   // first one is angular buildin ng-click, so we remove it.
   $delegate.shift();
   return $delegate;
   }]);
});

// add custom single click directive: make ngClick to only trigger if not double click
myApp.directive('ngClick', ['$parse', '$timeout', dirSingleClickExclusive]); 

function dirSingleClickExclusive($parse, $timeout) {
  return {
    restrict: 'A',
    replace : false,
    priority: 99, // after all build-in directive are compiled
    link: link
  }
	
  function link ($scope, element, attrs) {
    const delay = 400;
    var clicked = false, cancelClick = false;
    var user_function = $parse(attrs['ngClick']); //(scope);
	
    element.on('click', function (e) {
      // Adapted from: https://stackoverflow.com/a/29073481/4352306
      if (clicked) cancelClick = true; // it is not a single click
      clicked = true;
      
      if (!cancelClick) { // prevent a second timeout
        $timeout(function () { // for time window between clicks (delay)
          if (cancelClick) {
            clicked = false; cancelClick = false;
            return;
          }
          $scope.$apply(function () {
            user_function($scope, {$event : e});
          });
  				
          // reset click status
          clicked = false; cancelClick = false;
        }, delay);
      }
    });
  }
}
})(window.angular);
&#13;
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example - custom single click</title>
  
  <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
  <script src="app.js"></script>
  
</head>
<body ng-app="myApp">
  <div ng-controller="myCtrl">
   <button ng-click="singleClick()" ng-dblclick="doubleClick()">Click me!</button>
   <p ng-if="click">This was a {{click}} click.</p>
  </div>
</body>
</html>
&#13;
&#13;
&#13;

答案 5 :(得分:0)

如果singleClick上的doubleClick调用没有任何错误

,它也会有效
<div 
    onclick="var scope = angular.element(this).scope(); scope.singleClick();"
    ng-click="null" 
    ng-dblclick="doubleClick()"
></div>