Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to highlight drop zone under draggable element using angularJS?

I'm having issues to highlight the drop zone (defined with dropZone directive) when the dragged element is above it.

I've tried to use CSS:

.highlight {
    background-color: rgba(0, 255, 0, 0.2);
}
.highlight:hover {
    background-color: rgba(0, 255, 0, 0.5);
}

But this doesn't work because I'm dragging an element so the hover is on the draggable element.

Here is the code:

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>

Is it possible only using CSS ?

like image 265
Elfayer Avatar asked Aug 03 '15 12:08

Elfayer


3 Answers

I found a solution to my problem. Thanks to your answers, I found another way through.

I'm listening to the mousemove event on the dropZone directive. This way if the mouse moves inside the drop zone it will trigger and add the style.

Here is the working code:

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>
like image 170
Elfayer Avatar answered Nov 17 '22 21:11

Elfayer


You can use document.elementFromPoint to identify the element the cursor is on. Now you just have to verify that it has the 'highlight' class.

btw - If you'll use translate to move the element image, the document.elementFromPoint will have better results, because the dragged element won't interfere.

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"
    });
}
like image 2
Ori Drori Avatar answered Nov 17 '22 22:11

Ori Drori


Is it possible only using CSS ?

The truth is, it is up to the browser to decide if an element is hovered or not. So if he says NO, then I'm not sure if CSS would be enough if :hover or :active won't be triggered in the first place.

Anyway, this is one more JS solution using Angular's built-in behaviors :


ngMouseenter + ngMouseleave

Add this to your CSS :

.hovered {
  background-color: rgba(0, 255, 0, 0.5);
  cursor: pointer;
  z-index: 999;
}

Then use mouseenter and mouseleave behaviors inside your dropZone directive like so :

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');
      });

    }
  };
});

This Snippet shows how it works :

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>
like image 1
Salem Ouerdani Avatar answered Nov 17 '22 22:11

Salem Ouerdani