如何使用mousedown.dragselect事件捕获表td元素?

时间:2022-02-13 00:01:42

I have a directive which renders a HTML table where each td element has an id

我有一个指令,它呈现一个HTML表,其中每个td元素都有一个id

What I want to accomplish is to use the mousedown.dragselect/mouseup.dragselect to determine which elements have been selected, and then highlight those selected elements. What I have so far is something like this:

我想要完成的是使用mousedown.dragselect / mouseup.dragselect来确定选择了哪些元素,然后突出显示那些选定的元素。到目前为止我所拥有的是这样的:

var $ele = $(this);
scope.bindMultipleSelection = function() {
  element.bind('mousedown.dragselect', function() {
    $document.bind('mousemove.dragselect', scope.mousemove);
    $document.bind('mouseup.dragselect', scope.mouseup);
  });
};

scope.bindMultipleSelection();

scope.mousemove = function(e) {
  scope.selectElement($(this));
};

scope.mouseup = function(e) {
};

scope.selectElement = function($ele) {
  if (!$ele.hasClass('eng-selected-item'))
    $ele.addClass('eng-selected-item'); //apply selection or de-selection to current element
};

How can I get every td element selected by mousedown.dragselect, and be able to get their ids and then highlight them?

如何获取mousedown.dragselect选择的每个td元素,并能够获取它们的ID然后突出显示它们?

1 个解决方案

#1


12  

I suspect using anything relating to dragging won't give you what you want. Dragging is actually used when moving elements about (e.g. dragging files in My Computer / Finder), when what you're after is multiple selection.

我怀疑使用任何与拖动有关的东西都不会给你你想要的东西。拖动实际上是在移动元素时使用(例如,在“我的电脑/搜索器”中拖动文件),当你所追求的是多重选择时。

So there a number of things the directive needs:

所以指令需要很多东西:

  • Listen to mousedown, mouseenter and mouseup, events.

    听mousedown,mouseenter和mouseup,事件。

    • mousedown should listen on the cells of the table, and set a "dragging" mode.
    • mousedown应该监听表格的单元格,并设置“拖动”模式。
    • mouseenter should listen on the cells as well, and if the directive is in dragging mode, select the "appropriate cells"
    • mouseenter也应该监听单元格,如果指令处于拖动模式,选择“适当的单元格”
    • mouseup should disable dragging mode, and actually be on the whole body, in case the mouse is lifted up while the cursor is not over the table.
    • mouseup应该禁用拖动模式,并且实际上是在整个身体上,以防鼠标在光标不在桌面上时被抬起。
  • jQuery delegation is useful here, as it can nicely delegate the above events to the table, so the code is much more friendly to cells that are added after this directive is initialised. (I wouldn't include or use jQuery in an Angular project unless you have a clear reason like this).

    jQuery委托在这里很有用,因为它可以很好地将上述事件委托给表,因此代码对初始化此指令后添加的单元格更加友好。 (除非你有明确的理由,否则我不会在Angular项目中包含或使用jQuery)。

  • Although you've not mentioned it, the "appropriate cells" I suspect all the cells "between" where the mouse was clicked, and the current cell, chosen in a rectangle, and not just the cells that have been entered while the mouse was held down. To find these, cellIndex and rowIndex can be used, together with filtering all the cells from the table.

    虽然你没有提到它,但是“适当的细胞”我怀疑点击鼠标的所有单元格和在矩形中选择的当前单元格,而不仅仅是鼠标所在的单元格。压制住了。要找到这些,可以使用cellIndex和rowIndex,同时过滤表中的所有单元格。

  • All the listeners should be wrapped $scope.$apply to make sure Angular runs a digest cycle after they fire.

    所有的监听器都应该包含$ scope。$ apply以确保Angular在它们触发后运行一个摘要周期。

  • For the directive to communicate the ids of the selected elements to the surrounding scope, the directive can use bi-directional binding using the scope property, and the = symbol, as explained in the Angular docs

    对于将所选元素的id传递给周围范围的指令,该指令可以使用scope属性和=符号进行双向绑定,如Angular文档中所述。

Putting all this together gives:

把所有这些放在一起给出了:

app.directive('dragSelect', function($window, $document) {
  return {
    scope: {
      dragSelectIds: '='
    },
    controller: function($scope, $element) {
      var cls = 'eng-selected-item';
      var startCell = null;
      var dragging = false;

      function mouseUp(el) {
        dragging = false;
      }

      function mouseDown(el) {
        dragging = true;
        setStartCell(el);
        setEndCell(el);
      }

      function mouseEnter(el) {
        if (!dragging) return;
        setEndCell(el);
      }

      function setStartCell(el) {
        startCell = el;
      }

      function setEndCell(el) {
        $scope.dragSelectIds = [];
        $element.find('td').removeClass(cls);
        cellsBetween(startCell, el).each(function() {
          var el = angular.element(this);
          el.addClass(cls);
          $scope.dragSelectIds.push(el.attr('id'));
        });
      }

      function cellsBetween(start, end) {
        var coordsStart = getCoords(start);
        var coordsEnd = getCoords(end);
        var topLeft = {
          column: $window.Math.min(coordsStart.column, coordsEnd.column),
          row: $window.Math.min(coordsStart.row, coordsEnd.row),
        };
        var bottomRight = {
          column: $window.Math.max(coordsStart.column, coordsEnd.column),
          row: $window.Math.max(coordsStart.row, coordsEnd.row),
        };
        return $element.find('td').filter(function() {
          var el = angular.element(this);
          var coords = getCoords(el);
          return coords.column >= topLeft.column
              && coords.column <= bottomRight.column
              && coords.row >= topLeft.row
              && coords.row <= bottomRight.row;
        });
      }

      function getCoords(cell) {
        var row = cell.parents('row');
        return {
          column: cell[0].cellIndex, 
          row: cell.parent()[0].rowIndex
        };
      }

      function wrap(fn) {
        return function() {
          var el = angular.element(this);
          $scope.$apply(function() {
            fn(el);
          });
        }
      }

      $element.delegate('td', 'mousedown', wrap(mouseDown));
      $element.delegate('td', 'mouseenter', wrap(mouseEnter));
      $document.delegate('body', 'mouseup', wrap(mouseUp));
    }
  }
});

Another thing that will make the experience a bit nicer, is to set the cursor to a pointer, and disable text selection

使体验更好的另一件事是将光标设置为指针,并禁用文本选择

[drag-select] {
  cursor: pointer;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

You can also see this in action in this working demo

您还可以在此工作演示中看到这一点

#1


12  

I suspect using anything relating to dragging won't give you what you want. Dragging is actually used when moving elements about (e.g. dragging files in My Computer / Finder), when what you're after is multiple selection.

我怀疑使用任何与拖动有关的东西都不会给你你想要的东西。拖动实际上是在移动元素时使用(例如,在“我的电脑/搜索器”中拖动文件),当你所追求的是多重选择时。

So there a number of things the directive needs:

所以指令需要很多东西:

  • Listen to mousedown, mouseenter and mouseup, events.

    听mousedown,mouseenter和mouseup,事件。

    • mousedown should listen on the cells of the table, and set a "dragging" mode.
    • mousedown应该监听表格的单元格,并设置“拖动”模式。
    • mouseenter should listen on the cells as well, and if the directive is in dragging mode, select the "appropriate cells"
    • mouseenter也应该监听单元格,如果指令处于拖动模式,选择“适当的单元格”
    • mouseup should disable dragging mode, and actually be on the whole body, in case the mouse is lifted up while the cursor is not over the table.
    • mouseup应该禁用拖动模式,并且实际上是在整个身体上,以防鼠标在光标不在桌面上时被抬起。
  • jQuery delegation is useful here, as it can nicely delegate the above events to the table, so the code is much more friendly to cells that are added after this directive is initialised. (I wouldn't include or use jQuery in an Angular project unless you have a clear reason like this).

    jQuery委托在这里很有用,因为它可以很好地将上述事件委托给表,因此代码对初始化此指令后添加的单元格更加友好。 (除非你有明确的理由,否则我不会在Angular项目中包含或使用jQuery)。

  • Although you've not mentioned it, the "appropriate cells" I suspect all the cells "between" where the mouse was clicked, and the current cell, chosen in a rectangle, and not just the cells that have been entered while the mouse was held down. To find these, cellIndex and rowIndex can be used, together with filtering all the cells from the table.

    虽然你没有提到它,但是“适当的细胞”我怀疑点击鼠标的所有单元格和在矩形中选择的当前单元格,而不仅仅是鼠标所在的单元格。压制住了。要找到这些,可以使用cellIndex和rowIndex,同时过滤表中的所有单元格。

  • All the listeners should be wrapped $scope.$apply to make sure Angular runs a digest cycle after they fire.

    所有的监听器都应该包含$ scope。$ apply以确保Angular在它们触发后运行一个摘要周期。

  • For the directive to communicate the ids of the selected elements to the surrounding scope, the directive can use bi-directional binding using the scope property, and the = symbol, as explained in the Angular docs

    对于将所选元素的id传递给周围范围的指令,该指令可以使用scope属性和=符号进行双向绑定,如Angular文档中所述。

Putting all this together gives:

把所有这些放在一起给出了:

app.directive('dragSelect', function($window, $document) {
  return {
    scope: {
      dragSelectIds: '='
    },
    controller: function($scope, $element) {
      var cls = 'eng-selected-item';
      var startCell = null;
      var dragging = false;

      function mouseUp(el) {
        dragging = false;
      }

      function mouseDown(el) {
        dragging = true;
        setStartCell(el);
        setEndCell(el);
      }

      function mouseEnter(el) {
        if (!dragging) return;
        setEndCell(el);
      }

      function setStartCell(el) {
        startCell = el;
      }

      function setEndCell(el) {
        $scope.dragSelectIds = [];
        $element.find('td').removeClass(cls);
        cellsBetween(startCell, el).each(function() {
          var el = angular.element(this);
          el.addClass(cls);
          $scope.dragSelectIds.push(el.attr('id'));
        });
      }

      function cellsBetween(start, end) {
        var coordsStart = getCoords(start);
        var coordsEnd = getCoords(end);
        var topLeft = {
          column: $window.Math.min(coordsStart.column, coordsEnd.column),
          row: $window.Math.min(coordsStart.row, coordsEnd.row),
        };
        var bottomRight = {
          column: $window.Math.max(coordsStart.column, coordsEnd.column),
          row: $window.Math.max(coordsStart.row, coordsEnd.row),
        };
        return $element.find('td').filter(function() {
          var el = angular.element(this);
          var coords = getCoords(el);
          return coords.column >= topLeft.column
              && coords.column <= bottomRight.column
              && coords.row >= topLeft.row
              && coords.row <= bottomRight.row;
        });
      }

      function getCoords(cell) {
        var row = cell.parents('row');
        return {
          column: cell[0].cellIndex, 
          row: cell.parent()[0].rowIndex
        };
      }

      function wrap(fn) {
        return function() {
          var el = angular.element(this);
          $scope.$apply(function() {
            fn(el);
          });
        }
      }

      $element.delegate('td', 'mousedown', wrap(mouseDown));
      $element.delegate('td', 'mouseenter', wrap(mouseEnter));
      $document.delegate('body', 'mouseup', wrap(mouseUp));
    }
  }
});

Another thing that will make the experience a bit nicer, is to set the cursor to a pointer, and disable text selection

使体验更好的另一件事是将光标设置为指针,并禁用文本选择

[drag-select] {
  cursor: pointer;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

You can also see this in action in this working demo

您还可以在此工作演示中看到这一点