从 JQuery-UI 包含中排除元素

Exclude element from JQuery-UI containment

我试图让一个元素 dragable/resizable 在另一个元素的内部,但不在该元素的另一个子元素的区域内。

给定以下 table 行:

我希望能够在 table 行中的任意位置拖动蓝色区域并调整其大小,而不是灰色 table 单元格所在的位置(即它会 move/resize 在其中自由前四个单元格,但在到达最后一个单元格时停止。

我可以使用 containment: '.middle tr'containment: $el.closest('tr'),(其中 $el 是蓝色元素的选择器)选项将其限制在 table 行 [=18] =] 和 JQuery.resizable,但我一直无法找到一种方法来从包含中排除最后一列。

如何从包含区域中排除元素?

代码示例:

$(function() {
  $("td").droppable({
    drop: function(event, ui) {
      var draggable = ui.draggable;

      var newLeft = draggable.offset().left - draggable.closest(".middle table").offset().left;
      var childNum = Math.round((newLeft + 100) / 100);

      var newContainer = $(this).closest('tr').find('td:nth-of-type(' + childNum + ')');

      draggable.css({
        top: '',
        left: ''
      });

      newContainer.append(draggable);
    },
  });
  $(".planning-spot").resizable({
    grid: [100, 10000000],
    handles: "e",
    containment: ".middle tr"
  }).each(function() {
    var $el = $(this);
    $el.draggable({
      containment: $el.closest('tr'),
      axis: 'x',
    });
  });
});
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

table {
  border-collapse: collapse;
  overflow: hidden;
  table-layout: fixed;
  width: 500px;
  margin: 20px;
}

tr {
  position: relative;
  background: #FFF;
}

td, th {
  position: relative;
  padding: 0;
  width: 100px;
  height: 30px;
  border: 1px solid #000;
}

.planning-spot {
  position: absolute;
  top: 0;
  left: 0;
  height: 20px;
  width: 90px;
  border-radius: 10px;
  margin: 5px;
  text-align: center;
  z-index: 1;
  cursor: pointer;
  color: #FFF;
  width: 290px;
  background: #3CF;
}

.no-planning {
  background: #CCC;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<link href="https://code.jquery.com/ui/1.12.1/jquery-ui.min.css" rel="stylesheet"/>
<div class="wrapper">
    <div class="middle">
      <table>
        <tbody>
          <tr>
            <td>
              <div></div>
              <div class="planning-spot">18 / 20</div>
            </td>
            <td>
              <div></div>
            </td>
            <td>
              <div></div>
            </td>
            <td>
              <div></div>
            </td>
            <td class="no-planning">
              <div></div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
</div>

http://jsfiddle.net/xpvt214o/668937/

我知道我可以在 droppableresizabledropstop 函数中分别检查位置,并在需要时反转操作(是我当前的解决方案),但我正在寻找一种解决方案,以防止它首先被移动到 "unwanted" 区域。

我通过检查拖动事件是否会导致与不可拖动的项目发生碰撞来解决这个问题。

结帐代码如下:

$(function() {
 var nonDragableItems = $(".no-planning");
  $("td").droppable({
    drop: function(event, ui) {
      var draggable = ui.draggable;

      var newLeft = draggable.offset().left - draggable.closest(".middle table").offset().left;
      var childNum = Math.round((newLeft + 100) / 100);

      var newContainer = $(this).closest('tr').find('td:nth-of-type(' + childNum + ')');

      draggable.css({
        top: '',
        left: ''
      });

      newContainer.append(draggable);
    },
  });
  
  function onDrag(e, ui){
    var dragElemRect = ui.helper[0].getBoundingClientRect();
    for(var i=0;i<nonDragableItems.length;i++){
      var elemRect = nonDragableItems[i].getBoundingClientRect();
      if(areColliding(dragElemRect, elemRect))
        return false;
    }
  }

  function areColliding(rect1, rect2){
    if(rect1.bottom <= rect2.top ||
       rect1.top >= rect2.bottom ||
       rect1.left >= rect2.right ||
       rect1.right <= rect2.left)
          return false;
        return true;
  }
  
  $(".planning-spot").resizable({
    grid: [100, 10000000],
    handles: "e",
    containment: ".middle tr"
  }).each(function() {
    var $el = $(this);
    $el.draggable({
      containment: $el.closest('tr'),
      axis: 'x',
      drag: onDrag
    });
  });
});
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

table {
  border-collapse: collapse;
  overflow: hidden;
  table-layout: fixed;
  width: 500px;
  margin: 20px;
}

tr {
  position: relative;
  background: #FFF;
}

td, th {
  position: relative;
  padding: 0;
  width: 100px;
  height: 30px;
  border: 1px solid #000;
}

.planning-spot {
  position: absolute;
  top: 0;
  left: 0;
  height: 20px;
  width: 90px;
  border-radius: 10px;
  margin: 5px;
  text-align: center;
  z-index: 1;
  cursor: pointer;
  color: #FFF;
  width: 290px;
  background: #3CF;
}

.no-planning {
  background: #CCC;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" rel="stylesheet"/>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>

<div class="wrapper">
    <div class="middle">
      <table>
        <tbody>
          <tr>
            <td>
              <div></div>
              <div class="planning-spot">18 / 20</div>
            </td>
            <td>
              <div></div>
            </td>
            <td>
              <div></div>
            </td>
            <td>
              <div></div>
            </td>
            <td class="no-planning">
              <div></div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
</div>

您可以使用 getBoundingClientRect() 确定可拖动区域并将容器分配给一个数组,该数组占用每个可调整大小元素的宽度

$(function() {

  $("td").droppable({
    drop: function(event, ui) {
      var draggable = ui.draggable;

      var newLeft = draggable.offset().left - draggable.closest(".middle table").offset().left;
      var childNum = Math.round((newLeft + 100) / 100);

      var newContainer = $(this).closest('tr').find('td:nth-of-type(' + childNum + ')');

      draggable.css({
        top: '',
        left: ''
      });

      newContainer.append(draggable);
    },
  });
  $(".planning-spot").resizable({
    grid: [100, 10000000],
    handles: "e",
    containment: ".middle tr"
  }).each(function() {
    var $el = $(this);
    var bBoxTr = $el.closest('tr')[0].getBoundingClientRect();
    var bBoxTd = $el.closest('tr').children(".no-planning")[0].getBoundingClientRect();
    var x1 = bBoxTr.x, y1 = bBoxTr.y;
    var x2 = bBoxTr.right-bBoxTd.width, y2 = bBoxTr.bottom;
    $el.draggable({
//      containment: $el.closest('tr'),
      containment: [x1,y1,x2-$el.width()-10,y2],
      axis: 'x',
    });
  });
});
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

table {
  border-collapse: no-collapse;
  overflow: hidden;
  table-layout: fixed;
  width: 500px;
  margin: 20px;
}

tr {
  position: relative;
  background: #FFF;
}

td, th {
  position: relative;
  padding: 0;
  width: 100px;
  height: 30px;
  border: 1px solid #000;
}

.planning-spot {
  position: absolute;
  top: 0;
  left: 0;
  height: 20px;
  width: 90px;
  border-radius: 10px;
  margin: 5px;
  text-align: center;
  z-index: 1;
  cursor: pointer;
  color: #FFF;
  width: 290px;
  background: #3CF;
}

.no-planning {
  background: #CCC;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<link href="https://code.jquery.com/ui/1.12.1/jquery-ui.min.css" rel="stylesheet"/>
<div class="wrapper">
    <div class="middle">
      <table>
        <tbody>
          <tr id="test">
            <td>
              <div></div>
              <div class="planning-spot">18 / 20</div>
            </td>
            <td>
              <div></div>
            </td>
            <td>
              <div></div>
            </td>
            <td>
              <div></div>
            </td>
            <td class="no-planning">
              <div></div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
</div>

编辑:

由于并非所有浏览器都支持 getBoundingClientRect();,因此最好使用 jquery 功能,position()height()width();

请参阅下面修改后的代码段,函数 getDragArea returns 一个包含拖动区域坐标的数组。我添加了一个侦听器(鼠标悬停),它允许在需要时重置拖动区域。 table 包裹在滚动 div 中以测试行为。

$(function() {

  $("td").droppable({
    drop: function(event, ui) {
      var draggable = ui.draggable;

      var newLeft = draggable.offset().left - draggable.closest(".middle table").offset().left;
      var childNum = Math.round((newLeft + 100) / 100);

      var newContainer = $(this).closest('tr').find('td:nth-of-type(' + childNum + ')');

      draggable.css({
        top: '',
        left: ''
      });

      newContainer.append(draggable);
    },
  });
  $(".planning-spot").resizable({
    grid: [100, 10000000],
    handles: "e",
    containment: ".middle tr"
  }).each(function() {
    $(this).on('mouseover',function(){
      $(this).draggable({
      containment: getDragArea($(this)),
      axis: 'x',
    });
 })
  });
  
 function getDragArea($el){
    var $tr = $el.closest('tr');
   var $tds = $el.closest('tr').children(".no-planning");
   var width = $el.closest('tr').width()-$tds.width()*$tds.length;
   var height = $el.closest('tr').height();
    var position = $el.closest('tr').position();
    var x1 = position.left, y1 = position.top;
    var x2 = x1+width, y2 = y1+height;
    return [x1,y1,x2-$el.width()-10,y2]
   }
});
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

table {
  border-collapse: no-collapse;
  overflow: hidden;
  table-layout: fixed;
  width: 500px;
  margin: 20px;
}

tr {
  position: relative;
  background: #FFF;
}

td, th {
  position: relative;
  padding: 0;
  width: 100px;
  height: 30px;
  border: 1px solid #000;
}

.planning-spot {
  position: absolute;
  top: 0;
  left: 0;
  height: 20px;
  width: 90px;
  border-radius: 10px;
  margin: 5px;
  text-align: center;
  z-index: 1;
  cursor: pointer;
  color: #FFF;
  width: 290px;
  background: #3CF;
}

.no-planning {
  background: #CCC;
}

.wrapper{
  overflow:auto;
  width:500px;
  height:120px;
  border: solid 1px red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<link href="https://code.jquery.com/ui/1.12.1/jquery-ui.min.css" rel="stylesheet"/>
<div class="wrapper">
    <div class="middle">
      <table>
        <tbody>
          <tr id="test">
            <td>
              <div></div>
              <div class="planning-spot">18 / 20</div>
            </td>
            <td>
              <div></div>
            </td>
            <td>
              <div></div>
            </td>
            <td>
              <div></div>
            </td>
            <td class="no-planning">
              <div></div>
            </td>
            <td class="no-planning">
              <div></div>
            </td>
          </tr>
        </tbody>
      </table>
      <br>
      <br>
      <br>
      <br>
      <br>
      <br>
      <br>
      <br>
    </div>
</div>