angular-drag-and-drop-lists icon indicating copy to clipboard operation
angular-drag-and-drop-lists copied to clipboard

Cursor position of inputs not changable by mouse in FF and IE

Open slm0n opened this issue 8 years ago • 12 comments

Clicking on an input (I tried text, text area) sets the cursor to index 0 for FF and IE. Additionally, under IE a double click is required to get a cursor. Chrome and Safari work as expected (i.e. the cursor is set to the position next to the mouse).

Can be reproduced with the "types" demo: http://marceljuenemann.github.io/angular-drag-and-drop-lists/demo/#/types

slm0n avatar Sep 04 '15 05:09 slm0n

+1 I can verify this issue is happening.

dben avatar Sep 11 '15 19:09 dben

Any workarounds for this?

gummdy avatar Jan 21 '16 15:01 gummdy

If you do not need the item to be draggable while the input field is displayed, you can set the draggable attribute to false on the dnd-draggable element.

<div dnd-list="myList">
  <div ng-repeat="item in myList" dnd-draggable="item" draggable="{{!item.editing}}">
    <span ng-if="item.editing"><input ng-model="item.name"> <a href ng-click="item.editing = false">Save</a></span>
    <span ng-if="!item.editing">{{item.name}} <a href ng-click="item.editing = true">Edit</a></span>
  </div>
</div>

Tested in FF 43 and IE 10/11.

I think the issue occurs because text boxes/areas automatically become drop zones. The issue seems to be a problem with how FF and IE implemented HTML5 drag and drop.

nkoded avatar Jan 25 '16 21:01 nkoded

Thanks for the response. I did get this to work by conditionally setting dnd-disable-if. This little trick won't work if the input element is already inside of a dnd-nodrag (which I require to keep users from accidentally dragging the container while trying to edit the field).

gummdy avatar Jan 29 '16 21:01 gummdy

Seems like the demo have the same problem in FF : http://marceljuenemann.github.io/angular-drag-and-drop-lists/demo/#/types

NoZiL avatar Apr 28 '16 16:04 NoZiL

I had some problem similar. I think that was related to this kind of problem: https://connect.microsoft.com/IE/feedback/details/927470/ie-11-input-field-of-type-text-does-not-respond-to-mouse-clicks-when-ancestor-node-has-draggable-true I solve the problem, making sure that input parents were not draggable="true", so I needed to add in dndNoDrag directive this code:

.directive('dndNodrag', function() { return function(scope, element, attr) { // Set as draggable so that we can cancel the events explicitly element.attr("draggable", "true");

  // If the dnd-disable-if attribute is set, we have to watch that
  if (attr.dndDisableIf) {
    scope.$watch(attr.dndDisableIf, function(disabled) {
      element.attr("draggable", !disabled);
    });
  }

cvelasco88 avatar Aug 01 '16 10:08 cvelasco88

I can reproduce this issue on FF51 and IE11. Text inputs do not respond to mouse clicks as expected if they are contained in a dnd-draggable element. Any ancestor element with draggable="true" triggers this bug.

dnd-nodrag does not help.

Issues #273, #403, #242, #389 seem to be duplicates.

The following directive is a kludgy workaround. It sets all "draggable" attributes to false on mouseover, and restores them on mouseout.

Caveats:

  • Touch devices do not have mouseover, this is not tested on mobile.
  • If you have styles relying on the "draggable" attribute, you have to change them.

To use the directive, add it to the input element: <input type="text" dnd-nodrag-mouseover>

.directive('dndNodragMouseover', function(){
  return {
    restrict: 'A', 
    require: 'dndNodragMouseover', 
    controller: function ( $element ) {
      
      this.ancestors = [];
      
      this.findDraggableAncestorsUntilDndDraggable = function ( h ) {
        var a = [];
        while ( h !== null ) {
          if ( h.attr('draggable') !== undefined ) {
            a.push({ 
              element : h, 
              draggable : h.attr('draggable')
            });
          }
          if ( h.attr('dnd-draggable') !== undefined ) {
            break;
          }
          h = h.parent();
        }
        return a;
      };
      
      this.cleanup = function () {
        this.ancestors = [];
      };
      
      this.removeDraggable = function () {
        this.ancestors = this.findDraggableAncestorsUntilDndDraggable( $element );
        this.ancestors.forEach(function(o){
          o.element.attr('draggable', 'false');
        });
      };
      
      this.restoreDraggable = function () {
        this.ancestors.forEach(function(o){
          o.element.attr('draggable', o.draggable);
        });
      };
      
    }, 
    link: function (scope, iElement, iAttrs, controller) {
      iElement.on('mouseover', function(event){
        controller.removeDraggable();
      });
      iElement.on('mouseout', function(event){
        controller.restoreDraggable();
      });
      scope.$on('$destroy', function(){
        iElement.off('mouseover');
        iElement.off('mouseout');
        controller.cleanup();
      });
    }
  };
})

timostamm avatar Apr 19 '17 11:04 timostamm

Thanks @timostamm, this seems like the best solution at the moment.

namoscato avatar Apr 30 '17 22:04 namoscato

@timostamm I'm trying to implement your solution, but i get the following error: Controller 'dndNodragMouseover', required by directive 'dndNodragMouseover', can't be found!

<input type="text" dnd-nodrag-mouseover class="form-control" ng-model="...">

Any idea?

SebSob avatar May 09 '19 09:05 SebSob

@SebSob I am still using exactly the same code in a project. It is still on angular ~1.4.6. I suggest to double check for typos (the require must match the directive name). If this does not work, then maybe there is a breaking change in newer angular js versions?

I suggest trying to isolate the problem. require: 'dndNodragMouseover' just makes the controller available to the link function. That should definitely still be possible in later angular versions.

If you can't get it to work, just refactor the code. Remove the "require" and put the controller logic into the link function.

timostamm avatar May 09 '19 10:05 timostamm

@timostamm I had to rewrite the directive a little bit, because I use john papa's approach: (btw using angularjs 1.6.5)

Maybe you see the reason why it fails?

(function () {
    'use strict';

    angular
        .module('FormGenerator')
        .directive('dndNodragMouseover', dndNodragMouseoverDirective)

        function dndNodragMouseoverDirective() {
            
            var directive = {
                link: link,
                cntroller: controller,
                restrict: 'A',
                require: 'dndNodragMouseover',
            };
            
            return directive;
    
            // Controller function
            function controller ($element) {

                this.ancestors = [];

                this.findDraggableAncestorsUntilDndDraggable = function (h) {
                    var a = [];
                    while (h !== null) {
                        if (h.attr('draggable') !== undefined) {
                            a.push({
                                element: h,
                                draggable: h.attr('draggable')
                            });
                        }
                        if (h.attr('dnd-draggable') !== undefined) {
                            break;
                        }
                        h = h.parent();
                    }
                    return a;
                };

                this.cleanup = function () {
                    this.ancestors = [];
                };

                this.removeDraggable = function () {
                    this.ancestors = this.findDraggableAncestorsUntilDndDraggable($element);
                    this.ancestors.forEach(function (o) {
                        o.element.attr('draggable', 'false');
                    });
                };

                this.restoreDraggable = function () {
                    this.ancestors.forEach(function (o) {
                        o.element.attr('draggable', o.draggable);
                    });
                };

            }

            // Link function
            function link (scope, iElement, iAttrs, controller) {
                iElement.on('mouseover', function (event) {
                    controller.removeDraggable();
                });
                iElement.on('mouseout', function (event) {
                    controller.restoreDraggable();
                });
                scope.$on('$destroy', function () {
                    iElement.off('mouseover');
                    iElement.off('mouseout');
                    controller.cleanup();
                });
            }
        }

})();

It seems like the directive itself requires a controller (require: 'dndNodragMouseover'), which does not exist?

SebSob avatar May 09 '19 10:05 SebSob

@timostamm My bad, i did a typo cntroller: controller, I misspelled "controller", forgot the 'o'... :fearful: !

Now it seems to work, but double clicking in the input to select all the text still does not work, right? :thinking: And when I have the cursor in the input field I can still drag the element.

SebSob avatar May 09 '19 10:05 SebSob