如何使用angularJS突出显示可拖动元素下的拖放区?
Posted
技术标签:
【中文标题】如何使用angularJS突出显示可拖动元素下的拖放区?【英文标题】:How to highlight drop zone under draggable element using angularJS? 【发布时间】:2015-10-25 13:02:03 【问题描述】:当拖动的元素位于其上方时,我无法突出显示放置区域(使用 dropZone 指令定义)。
我尝试过使用 CSS:
.highlight
background-color: rgba(0, 255, 0, 0.2);
.highlight:hover
background-color: rgba(0, 255, 0, 0.5);
但这不起作用,因为我正在拖动一个元素,所以hover
在可拖动元素上。
代码如下:
var app = angular.module("myApp", []);
app.directive("dragCopy", function($http, $compile, $document)
return
restrict: 'A',
link: function($scope, $element)
$element.on("mousedown", function($event)
$event.preventDefault();
var newNode = $compile('<div class="dragFile" draggable-file>drag</div>')($scope);
newNode.children("#title").text($element.parent().text());
angular.element($document[0].body).append(newNode);
newNode.css(
top: $event.pageY - (newNode.prop("offsetHeight") * 0.9) + "px",
left: $event.pageX - (newNode.prop("offsetWidth") / 2) + "px",
);
newNode.triggerHandler("mousedown");
);
);
app.factory("dragDropService", function()
var object =
dropZoneList: [],
highlightList: [],
register: function(element)
object.dropZoneList.push(element);
,
highlightDropZones: function()
for (var i in object.dropZoneList)
var element = object.dropZoneList[i].append('<div class="highlight"></div>');
var childrens = element.children();
object.highlightList.push(childrens[childrens.length - 1]);
,
resetDropZones: function()
for (var i in object.highlightList)
object.highlightList[i].remove();
;
return object;
);
app.directive("dropZone", function(dragDropService)
return
restrict: 'A',
link: function($scope, $element)
dragDropService.register($element);
;
);
app.directive("draggableFile", function($document, dragDropService)
return
restrict: 'A',
link: function($scope, $element)
var startX = 0,
startY = 0;
var x, y;
$element.on("mousedown", function($event)
dragDropService.highlightDropZones();
startX = $element.prop("offsetWidth") / 2;
startY = $element.prop("offsetHeight") * 0.9;
$document.on("mousemove", mousemove);
$document.on("mouseup", mouseup);
);
function mousemove($event)
y = $event.pageY - startY;
x = $event.pageX - startX;
$element.css(
top: y + "px",
left: x + "px"
);
function mouseup()
$document.off("mousemove", mousemove);
$document.off("mouseup", mouseup);
$element.remove();
console.log(document.elementFromPoint(x, y));
dragDropService.resetDropZones();
);
.itemDrag
cursor: pointer;
border: 3px solid #81CFE0;
border-radius: 50%;
font-size: 40px;
color: #81CFE0;
padding: 5px;
background-color: rgba(255, 255, 255, 0.5);
#receiver
position: absolute;
left: 50%;
right: 0;
top: 0;
bottom: 0;
.dragFile
position: absolute;
border: 1px solid #81CFE0;
border-radius: 5px;
background-color: rgba(129, 207, 224, 0.5);
cursor: pointer;
.highlight
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
color: #00FF00;
border: 3px dashed #00FF00;
border-radius: 5px;
text-align: center;
font-weight: bold;
text-shadow: 0px 0px 2px black;
background-color: rgba(0, 255, 0, 0.2);
.highlight:before
content: "Add content to terminal";
.highlight:hover
background-color: rgba(0, 255, 0, 0.5);
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div class="itemDrag fa fa-hand-pointer-o" drag-copy></div>
<div id="receiver" drop-zone></div>
</div>
只能使用 CSS 吗?
【问题讨论】:
【参考方案1】:我找到了解决问题的方法。感谢您的回答,我找到了另一种方法。
我正在收听dropZone
指令上的mousemove
事件。这样,如果鼠标在放置区域内移动,它将触发并添加样式。
这是工作代码:
var app = angular.module("myApp", []);
app.directive("dragCopy", function($http, $compile, $document)
return
restrict: 'A',
link: function($scope, $element)
$element.on("mousedown", function($event)
$event.preventDefault();
var newNode = $compile('<div class="dragFile" draggable-file>drag</div>')($scope);
newNode.children("#title").text($element.parent().text());
angular.element($document[0].body).append(newNode);
newNode.css(
top: $event.pageY - (newNode.prop("offsetHeight") * 0.5) + "px",
left: $event.pageX - (newNode.prop("offsetWidth") / 2) + "px",
);
newNode.triggerHandler("mousedown");
);
);
app.factory("dragDropService", function()
var object =
dropZoneList: [],
callbackList: [],
highlightList: [],
register: function(element, callback)
object.dropZoneList.push(element);
object.callbackList.push(callback);
,
highlightDropZones: function()
for (var i in object.dropZoneList)
object.dropZoneList[i].append('<div class="highlight"></div>');
var childrens = object.dropZoneList[i].children();
object.highlightList.push(childrens[childrens.length - 1]);
object.callbackList[i]();
,
resetDropZones: function()
for (var i in object.highlightList)
object.callbackList[i]();
object.highlightList[i].remove();
object.highlightList = [];
;
return object;
);
app.directive("dropZone", function($document, dragDropService)
return
restrict: 'A',
link: function($scope, $element)
var highlighted = false;
function toggleHover()
if (highlighted)
$document.off("mousemove", mousemove);
highlighted = false;
else
$document.on("mousemove", mousemove);
highlighted = true;
function mousemove($event)
x = $event.pageX;
y = $event.pageY;
elementBounding = $element[0].getBoundingClientRect();
console.log("in");
if (x > elementBounding.left && x < elementBounding.right && y > elementBounding.top && y < elementBounding.bottom)
angular.element($element.children()[$element.children().length - 1]).addClass("droppable");
else
angular.element($element.children()[$element.children().length - 1]).removeClass("droppable");
dragDropService.register($element, toggleHover);
;
);
app.directive("draggableFile", function($document, dragDropService)
return
restrict: 'A',
link: function($scope, $element)
var startX = 0,
startY = 0;
var x, y;
$element.on("mousedown", function($event)
dragDropService.highlightDropZones();
startX = $element.prop("offsetWidth") / 2;
startY = $element.prop("offsetHeight") * 0.5;
$document.on("mousemove", mousemove);
$document.on("mouseup", mouseup);
);
function mousemove($event)
x = $event.pageX - startX;
y = $event.pageY - startY;
$element.css(
left: x + "px",
top: y + "px"
);
function mouseup()
$document.off("mousemove", mousemove);
$document.off("mouseup", mouseup);
$element.remove();
console.log(document.elementFromPoint(x, y));
dragDropService.resetDropZones();
;
);
.itemDrag
cursor: pointer;
border: 3px solid #81CFE0;
border-radius: 50%;
font-size: 40px;
color: #81CFE0;
padding: 5px;
background-color: rgba(255, 255, 255, 0.5);
#receiver1
position: absolute;
left: 50%;
right: 0;
top: 0;
bottom: 50%;
#receiver2
position: absolute;
left: 50%;
right: 0;
top: 50%;
bottom: 0;
.dragFile
position: absolute;
border: 1px solid #81CFE0;
border-radius: 5px;
background-color: rgba(129, 207, 224, 0.5);
cursor: pointer;
.highlight
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
color: #00FF00;
border: 3px dashed #00FF00;
border-radius: 5px;
text-align: center;
font-weight: bold;
text-shadow: 0px 0px 2px black;
background-color: rgba(0, 255, 0, 0.2);
.highlight:before
content: "Add content to terminal";
.highlight.droppable
background-color: rgba(0, 255, 0, 0.5);
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div class="itemDrag fa fa-hand-pointer-o" drag-copy></div>
<div id="receiver1" drop-zone></div>
<div id="receiver2" drop-zone></div>
</div>
【讨论】:
【参考方案2】:您可以使用 document.elementFromPoint 来识别光标所在的元素。现在您只需验证它是否具有“highlight”类。
btw - 如果你使用 translate 来移动元素图像,document.elementFromPoint 会有更好的结果,因为拖动的元素不会干扰。
function mousemove($event)
y = $event.pageY - startY;
x = $event.pageX - startX;
var dropElement = document.elementFromPoint(x, y);
console.log(dropElement.classList.contains('highlight'));
$element.css(
top : y + "px",
left : x + "px"
);
【讨论】:
不太方便,我希望有一个完整的 CSS 解决方案。 这是我所拥有的最好的。据我所知,当您的鼠标按下时,您无法将鼠标悬停在元素上,如果您将可拖动元素指针事件设置为无,它仍然不会激活悬停在其他元素上。【参考方案3】:只能使用 CSS 吗?
事实上,由浏览器决定元素是否悬停。所以如果他说不,那么我不确定如果:hover
或:active
不会首先被触发,CSS 是否足够。
无论如何,这是使用 Angular 内置行为的另一种 JS 解决方案:
ngMouseenter + ngMouseleave
将此添加到您的 CSS 中:
.hovered
background-color: rgba(0, 255, 0, 0.5);
cursor: pointer;
z-index: 999;
然后在 dropZone 指令中使用 mouseenter 和 mouseleave 行为,如下所示:
app.directive("dropZone", function(dragDropService)
return
restrict: 'A',
link: function($scope, $element)
dragDropService.register($element);
$element.bind("mouseenter", function(e)
// if mouse's button is not clicked then we are not dragging
if(e.buttons == 1 || e.buttons == 3)
$element.addClass('hovered');
);
$element.bind("mouseleave", function()
$element.removeClass('hovered');
);
;
);
这段代码展示了它是如何工作的:
var app = angular.module("myApp", []);
app.directive("dragCopy", function($http, $compile, $document)
return
restrict: 'A',
link: function($scope, $element)
$element.on("mousedown", function($event)
$event.preventDefault();
var newNode = $compile('<div class="dragFile" draggable-file>drag</div>')($scope);
newNode.children("#title").text($element.parent().text());
angular.element($document[0].body).append(newNode);
newNode.css(
top: $event.pageY - (newNode.prop("offsetHeight") * 0.9) + "px",
left: $event.pageX - (newNode.prop("offsetWidth") / 2) + "px",
);
newNode.triggerHandler("mousedown");
);
);
app.factory("dragDropService", function()
var object =
dropZoneList: [],
highlightList: [],
register: function(element)
object.dropZoneList.push(element);
,
highlightDropZones: function()
for (var i in object.dropZoneList)
var element = object.dropZoneList[i].append('<div class="highlight"></div>');
var childrens = element.children();
object.highlightList.push(childrens[childrens.length - 1]);
,
resetDropZones: function()
for (var i in object.highlightList)
object.highlightList[i].remove();
;
return object;
);
app.directive("dropZone", function(dragDropService)
return
restrict: 'A',
link: function($scope, $element)
dragDropService.register($element);
$element.bind("mouseenter", function(e)
if(e.buttons == 1 || e.buttons == 3)
$element.addClass('hovered');
);
$element.bind("mouseleave", function()
$element.removeClass('hovered');
);
;
);
app.directive("draggableFile", function($document, dragDropService)
return
restrict: 'A',
link: function($scope, $element)
var startX = 0,
startY = 0;
var x, y;
$element.on("mousedown", function($event)
dragDropService.highlightDropZones();
startX = $element.prop("offsetWidth") / 2;
startY = $element.prop("offsetHeight") * 0.9;
$document.on("mousemove", mousemove);
$document.on("mouseup", mouseup);
);
function mousemove($event)
y = $event.pageY - startY;
x = $event.pageX - startX;
$element.css(
top: y + "px",
left: x + "px"
);
function mouseup()
$document.off("mousemove", mousemove);
$document.off("mouseup", mouseup);
$element.remove();
console.log(document.elementFromPoint(x, y));
dragDropService.resetDropZones();
);
.itemDrag
cursor: pointer;
border: 3px solid #81CFE0;
border-radius: 50%;
font-size: 40px;
color: #81CFE0;
padding: 5px;
background-color: rgba(255, 255, 255, 0.5);
#receiver
position: absolute;
left: 50%;
right: 0;
top: 0;
bottom: 0;
.dragFile
position: absolute;
border: 1px solid #81CFE0;
border-radius: 5px;
background-color: rgba(129, 207, 224, 0.5);
cursor: pointer;
.highlight
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
color: #00FF00;
border: 3px dashed #00FF00;
border-radius: 5px;
text-align: center;
font-weight: bold;
text-shadow: 0px 0px 2px black;
background-color: rgba(0, 255, 0, 0.2);
.highlight:before
content: "Add content to terminal";
.hovered
background-color: rgba(0, 255, 0, 0.5);
cursor: pointer;
z-index: 999;
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div class="itemDrag fa fa-hand-pointer-o" drag-copy></div>
<div id="receiver" drop-zone></div>
</div>
【讨论】:
虽然这个想法看起来不错,但效果不太好!颜色在拖动元素上方发生变化,而不是在拖放区域下方。鼠标停留在可拖动元素上也不会触发,向下拖动时必须向外移动。 在可拖动区域内移动鼠标时,我必须添加z-index: 999;
以修复 ngMouseenter 事件的奇怪异常行为。既然您提到了它,我认为它可能会更好地使用 ngMouseover 代替,或者使用不同的 CSS 方法。稍后我会尝试优化它。但我不明白第二部分,doesn't trigger if the mouse stays on the draggable element, it has to go outside when dragging down.
是什么意思?您的意思是当您将元素拖放到其中而不必将鼠标移出时,dragZone 应该再次变为空白吗?
Do you mean that dragZone should became blank again when you Drop the element inside and not having to move the mouse out of it ?
- 这部分是。但我不是在谈论它。 mouseenter 事件仅在鼠标移动到on
和$element
时触发。因此,当鼠标位于可拖动元素上时,它不会显示 hovered
类。当我enter
$element
时,我必须快速移动鼠标,以便它可以脱离可拖动元素并显示hovered
类。 (我使用 Firefox 来处理这种行为)
嗯,我明白了。你是对的,不仅是 Chrome 中的 Firefox。除非您向下移动,否则当您缓慢进入内部时不会触发。我会弄清楚如何修复它并稍后进行更新。
我尝试过的所有解决方案都没有像我预期的那样工作,即使是纯 javascript onmouseenter
在拖动时的行为几乎相同,我认为基于 XY 坐标的解决方案在这种情况下会工作得更好,因为即使我知道如何让它工作我需要修改更多的东西,比如 object.dropZoneList[i].append('<div class="highlight"></div>');
这会破坏 mouseenter 事件,否则我需要使用元素 z-index
attr 或重建指令可能像 this one,所以在最后我发现@OriDrori 的想法更好。以上是关于如何使用angularJS突出显示可拖动元素下的拖放区?的主要内容,如果未能解决你的问题,请参考以下文章
拖动子元素时触发 Svelte 可拖动,onleave 事件