单击外部以使用按钮隐藏容器
Click outside to hide container with button
当用户单击或触摸外部时,我使用以下Angular指令来隐藏div元素。
https://github.com/TheSharpieOne/angular-off-click
它按预期工作,但当您在div外部单击切换div的按钮时,会触发"angular off click"回调以隐藏容器,但随后会调用该按钮附带的切换函数,重新打开div。
"关闭点击过滤器"通过添加异常来解决这个问题,这些异常在调用隐藏函数之前使用css选择器进行检查。然而,这被删除了,因为我们不希望在html标记中出现css类异常的额外膨胀。
所需的功能是当您点击容器外部时,切换按钮不会触发其处理程序
更新这只是触摸设备上的一个问题,默认情况下有300毫秒的延迟。因此,这意味着触发回调以隐藏容器,然后切换函数在300ms后运行,重新打开容器。在桌面上,点击鼠标,切换功能首先启动,然后回调
// Angular App Code
var app = angular.module('myApp', ['offClick']);
app.controller('myAppController', ['$scope', '$timeout', function($scope,$timeout) {
$scope.showContainer = false;
$scope.toggleContainer = function() {
$timeout(function() {
$scope.showContainer = !$scope.showContainer;
}, 300);
};
$scope.hideContainer = function(scope, event, p) {
$scope.showContainer = false;
console.log('event: ', event);
console.log('scope: ', scope);
console.log(p);
};
}]);
// Off Click Directive Code
angular.module('offClick', [])
.directive('offClick', ['$rootScope', '$parse', function ($rootScope, $parse) {
var id = 0;
var listeners = {};
// add variable to detect touch users moving..
var touchMove = false;
// Add event listeners to handle various events. Destop will ignore touch events
document.addEventListener("touchmove", offClickEventHandler, true);
document.addEventListener("touchend", offClickEventHandler, true);
document.addEventListener('click', offClickEventHandler, true);
function targetInFilter(target, elms) {
if (!target || !elms) return false;
var elmsLen = elms.length;
for (var i = 0; i < elmsLen; ++i) {
var currentElem = elms[i];
var containsTarget = false;
try {
containsTarget = currentElem.contains(target);
} catch (e) {
// If the node is not an Element (e.g., an SVGElement) node.contains() throws Exception in IE,
// see https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect
// In this case we use compareDocumentPosition() instead.
if (typeof currentElem.compareDocumentPosition !== 'undefined') {
containsTarget = currentElem === target || Boolean(currentElem.compareDocumentPosition(target) & 16);
}
}
if (containsTarget) {
return true;
}
}
return false;
}
function offClickEventHandler(event) {
// If event is a touchmove adjust touchMove state
if( event.type === 'touchmove' ){
touchMove = true;
// And end function
return false;
}
// This will always fire on the touchend after the touchmove runs...
if( touchMove ){
// Reset touchmove to false
touchMove = false;
// And end function
return false;
}
var target = event.target || event.srcElement;
angular.forEach(listeners, function (listener, i) {
if (!(listener.elm.contains(target) || targetInFilter(target, listener.offClickFilter))) {
$rootScope.$evalAsync(function () {
listener.cb(listener.scope, {
$event: event
});
});
}
});
}
return {
restrict: 'A',
compile: function ($element, attr) {
var fn = $parse(attr.offClick);
return function (scope, element) {
var elmId = id++;
var offClickFilter;
var removeWatcher;
offClickFilter = document.querySelectorAll(scope.$eval(attr.offClickFilter));
if (attr.offClickIf) {
removeWatcher = $rootScope.$watch(function () {
return $parse(attr.offClickIf)(scope);
}, function (newVal) {
if (newVal) {
on();
} else if (!newVal) {
off();
}
});
} else {
on();
}
attr.$observe('offClickFilter', function (value) {
offClickFilter = document.querySelectorAll(scope.$eval(value));
});
scope.$on('$destroy', function () {
off();
if (removeWatcher) {
removeWatcher();
}
element = null;
});
function on() {
listeners[elmId] = {
elm: element[0],
cb: fn,
scope: scope,
offClickFilter: offClickFilter
};
}
function off() {
listeners[elmId] = null;
delete listeners[elmId];
}
};
}
};
}]);
/* Styles go here */
.container {
background: blue;
color: #fff;
height: 300px;
width: 300px;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://code.angularjs.org/1.4.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body data-ng-app="myApp">
<h1>Hello Plunker!</h1>
<div data-ng-controller="myAppController">
<button data-ng-click="toggleContainer()">Toggle Container</button>
<div class="container" data-ng-show="showContainer" data-off-click="hideContainer()" data-off-click-if="showContainer">
This is the container
</div>
</div>
</body>
</html>
http://jsbin.com/hibovu
问题是,当你点击按钮时,两个功能都会启动:
- 指令中的
hideContainer
- 点击事件中的
toggleContainer
(再次显示div
)
解决方案
在评估hide
回调之前添加event.stopPropagation();
。
你是怎么做到的?
- 将事件传递给函数
data-off-click="hideContainer($event)"
- 在
$scope
中hideContainer
函数的定义中添加$event
参数,如下所示:$scope.hideContainer = function($event)
完整代码:
// Angular App Code
var app = angular.module('myApp', ['offClick']);
app.controller('myAppController', ['$scope', '$timeout', function($scope,$timeout) {
$scope.showContainer = false;
$scope.toggleContainer = function() {
$timeout(function() {
$scope.showContainer = !$scope.showContainer;
}, 300);
};
$scope.hideContainer = function($event) {
$event.stopPropagation();
$timeout(function(){
$scope.showContainer = false;
});
};
}]);
// Off Click Directive Code
angular.module('offClick', [])
.directive('offClick', ['$rootScope', '$parse', function ($rootScope, $parse) {
var id = 0;
var listeners = {};
// add variable to detect touch users moving..
var touchMove = false;
// Add event listeners to handle various events. Destop will ignore touch events
document.addEventListener("touchmove", offClickEventHandler, true);
document.addEventListener("touchend", offClickEventHandler, true);
document.addEventListener('click', offClickEventHandler, true);
function targetInFilter(target, elms) {
if (!target || !elms) return false;
var elmsLen = elms.length;
for (var i = 0; i < elmsLen; ++i) {
var currentElem = elms[i];
var containsTarget = false;
try {
containsTarget = currentElem.contains(target);
} catch (e) {
// If the node is not an Element (e.g., an SVGElement) node.contains() throws Exception in IE,
// see https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect
// In this case we use compareDocumentPosition() instead.
if (typeof currentElem.compareDocumentPosition !== 'undefined') {
containsTarget = currentElem === target || Boolean(currentElem.compareDocumentPosition(target) & 16);
}
}
if (containsTarget) {
return true;
}
}
return false;
}
function offClickEventHandler(event) {
// If event is a touchmove adjust touchMove state
if( event.type === 'touchmove' ){
touchMove = true;
// And end function
return false;
}
// This will always fire on the touchend after the touchmove runs...
if( touchMove ){
// Reset touchmove to false
touchMove = false;
// And end function
return false;
}
var target = event.target || event.srcElement;
angular.forEach(listeners, function (listener, i) {
if (!(listener.elm.contains(target) || targetInFilter(target, listener.offClickFilter))) {
//$rootScope.$evalAsync(function () {
listener.cb(listener.scope, {
$event: event
});
//});
}
});
}
return {
restrict: 'A',
compile: function ($element, attr) {
var fn = $parse(attr.offClick);
return function (scope, element) {
var elmId = id++;
var offClickFilter;
var removeWatcher;
offClickFilter = document.querySelectorAll(scope.$eval(attr.offClickFilter));
if (attr.offClickIf) {
removeWatcher = $rootScope.$watch(function () {
return $parse(attr.offClickIf)(scope);
}, function (newVal) {
if (newVal) {
on();
} else if (!newVal) {
off();
}
});
} else {
on();
}
attr.$observe('offClickFilter', function (value) {
offClickFilter = document.querySelectorAll(scope.$eval(value));
});
scope.$on('$destroy', function () {
off();
if (removeWatcher) {
removeWatcher();
}
element = null;
});
function on() {
listeners[elmId] = {
elm: element[0],
cb: fn,
scope: scope,
offClickFilter: offClickFilter
};
}
function off() {
listeners[elmId] = null;
delete listeners[elmId];
}
};
}
};
}]);
.container {
background: blue;
color: #fff;
height: 300px;
width: 300px;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body data-ng-app="myApp">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<h1>Hello Plunker!</h1>
<div data-ng-controller="myAppController">
<button data-ng-click="toggleContainer()">Toggle Container</button>
<div class="container" data-ng-show="showContainer" data-off-click="hideContainer($event)" data-off-click-if="showContainer">
This is the container
</div>
</div>
</body>
</html>
http://jsbin.com/hibovu/3/edit?html,css,js
相关文章:
- 剑道UI内联编辑:如何在点击其他按钮时隐藏按钮
- PHP Javascript显示/隐藏按钮不工作
- 如何隐藏按钮单击事件上的占位符
- 我的表单上的SpringWebFlow2中的每个转换都需要隐藏按钮吗
- 使用ng-hide根据条件显示/隐藏按钮
- 另一种显示和隐藏按钮的方式
- PHP If Else-隐藏按钮
- 如何显示/隐藏按钮
- jQuery 隐藏按钮,如果它们不包含文本
- 如何检测是否在主页上隐藏按钮
- 单击时隐藏按钮
- 在可丢弃的::javascript上显示隐藏按钮
- 想要使用onclick显示隐藏按钮
- 使用jQuery在ruby中显示/隐藏按钮
- 如果未输入URL,则隐藏按钮
- 当按下第二个按钮时如何隐藏按钮,以及当我点击页面的空白区域时如何使其出现
- 如何使用onmouseover和onmouseout在鼠标经过按钮时隐藏和取消隐藏按钮
- 使用运行时创建的链接来触发ASP.Net中隐藏按钮的单击处理程序
- 如何在ajax响应中隐藏按钮
- 切换时隐藏按钮