简体   繁体   English

在HTML表格上拖动选择(具有colspan / rowspan的Square / Rectangle部分)

[英]Drag Selection on HTML Table (Square/Rectangle Section with colspan/rowspan)

I'm working on THIS implementation for selecting cells in table, however in my case, cells can have a colspan or rowspan so the selection doesn't restrict to a square/rectangular selection (try selecting "1-3" and "2-3" which should also automatically select "1-4"). 我正在实现中选择表中的单元格,但是在我的情况下,单元格可以具有colspan或rowpan,因此选择不限于正方形/矩形选择(尝试选择“ 1-3”和“ 2- 3”,它也应该自动选择“ 1-4”)。 It's similar to THIS question, but I haven't been able to get anything to work. 这类似于问题,但是我无法使任何事情起作用。 Do you know how this would be implemented? 您知道如何实现吗?

Link: Working Code 链接: 工作代码

HTML HTML

<table drag-select drag-select-ids="ids">
      <tr>
        <td id="td-1-1">1-1</td>
        <td id="td-1-2">1-2</td>
        <td id="td-1-3">1-3</td>
        <td id="td-1-4">1-4</td>
      </tr>
      <tr>
        <td id="td-2-1">2-1</td>
        <td id="td-2-2">2-2</td>
        <td id="td-2-3" colspan="2">2-3</td>
      </tr>
      <tr>
        <td id="td-3-1">3-1</td>
        <td id="td-3-2">3-2</td>
        <td id="td-3-3">3-3</td>
        <td id="td-3-4">3-4</td>
      </tr>
      <tr>
        <td id="td-4-1">4-1</td>
        <td id="td-4-2">4-2</td>
        <td id="td-4-3">4-3</td>
        <td id="td-4-4">4-4</td>
      </tr>
    </table>

JavaScript JavaScript的

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.ids = [];
});

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

CSS CSS

[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;
}

[drag-select] .eng-selected-item {
  background: blue;
  color: white;
}

td {
  padding: 10px;
  border: 1px solid gray;
}

I used the x and y coordinates of the start and end cell, then calculate every cell that is inside the corresponding rectangle (even partially). 我使用了开始和结束单元格的x和y坐标,然后计算了相应矩形(甚至部分)内的每个单元格。 Then get the bounding rectangle for these cells and repeat the process until the selection is not expanding anymore. 然后获得这些单元格的边界矩形,并重复该过程,直到选择范围不再扩大为止。

EDIT: function rectangleSelect is (almost completely) from : Get DOM elements inside a rectangle area of a page 编辑:函数rectangleSelect是(几乎完全来自):从获取页面矩形区域内的DOM元素

EDIT 2: Now supporting rowspan and other edgecases like selecting [3-2,2-3]: 编辑2:现在支持行跨和其他边缘情况,如选择[3-2,2-3]: 在此处输入图片说明

http://plnkr.co/edit/8wZvcU1SgmieStsqg3lD?p=preview http://plnkr.co/edit/8wZvcU1SgmieStsqg3lD?p=preview

HTML: HTML:

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="jquery" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
    <script data-require="angular.js@1.2.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" data-semver="1.2.16"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <table drag-select drag-select-ids="ids">
      <tr>
        <td id="td-1-1">1-1</td>
        <td id="td-1-2">1-2</td>
        <td id="td-1-3">1-3</td>
        <td id="td-1-4">1-4</td>
      </tr>
      <tr>
        <td id="td-2-1" colspan=2>2-1</td>
        <td id="td-2-3" rowspan="2">2-3</td
        ><td id="td-2-4">2-2</td>
      </tr>
      <tr>
        <td id="td-3-1">3-1</td>
        <td id="td-3-2">3-2</td>
        <td id="td-3-4">3-4</td>
      </tr>
      <tr>
        <td id="td-4-1">4-1</td>
        <td id="td-4-2">4-2</td>
        <td id="td-4-3">4-3</td>
        <td id="td-4-4">4-4</td>
      </tr>
    </table>
    <p>Selected IDs: {{ids | json}}</p>
  </body>

</html>

CSS: unchanged CSS:不变

JS: JS:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.ids = [];
});

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 isPointBetween(point,x1,x2){
        return (point >=x1 && point <=x2) ||(point <=x1 && point>=x2);
      }
      function rectangleSelect(selector, bounds) {
    var elements = [];
    jQuery(selector).each(function() {
        var $this = jQuery(this);
        var offset = $this.offset();
        var x = offset.left;
        var y = offset.top;
        var w = $this.outerWidth();
        var h = $this.outerHeight();
        if ((isPointBetween(x,bounds.minX,bounds.maxX) && isPointBetween(y,bounds.minY,bounds.maxY))||
            (isPointBetween(x+w,bounds.minX,bounds.maxX) && isPointBetween(y+h,bounds.minY,bounds.maxY))
            ) {
            elements.push($this.get(0));
        }
    });
    return elements;
}
      function getBoundsForElements(elements){
        var x1= elements.reduce(function(currMinX,element){
          var elementLeft = $(element).offset().left;
          return currMinX && currMinX<elementLeft ? currMinX : elementLeft;
        },undefined);
        var x2= elements.reduce(function(currMaxX,element){
          var elementRight = $(element).offset().left+$(element).outerWidth();
          return currMaxX && currMaxX>elementRight ? currMaxX : elementRight;
        },undefined);
        var y1= elements.reduce(function(currMinY,element){
          var elementTop = $(element).offset().top;
          return currMinY && currMinY<elementTop ? currMinY : elementTop;
        },undefined);
        var y2= elements.reduce(function(currMaxY,element){
          var elementBottom = $(element).offset().top+$(element).outerHeight();
          return currMaxY && currMaxY>elementBottom ? currMaxY : elementBottom;
        },undefined);
        return {
          minX: x1,
          maxX: x2,
          minY: y1,
          maxY: y2
        };

      }


      function cellsBetween(start, end) {
        var bounds,elementsInside;
        elementsInside = [start,end];
        do{
          bounds = getBoundsForElements(elementsInside);
          var elementsInsideAfterExpansion = rectangleSelect("td",bounds);
          if(elementsInside.length==elementsInsideAfterExpansion.length)
            return elementsInside;
          else
            elementsInside=elementsInsideAfterExpansion;
        }while(true)

      }


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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM