3

I want to use a drag to select feature, but rather than select a square area on screen, and select anything within that square, I want to select any element between my initial choice and the one I release the click on.

This is best explained visually. I do not want to do this:

What I don't want to do

Instead I want to do this:

What I want to do

4

3 に答える 3

3

ボックスがリスト構造(ulまたはol)内にある場合は、マウスがドラッグされた最初と最後のボックスを検出し、それらの識別子を最初と最後のインデックスとして使用してコレクションを反復処理し、必要に応じて強調表示できます。

于 2012-06-23T10:12:18.777 に答える
1

After the previous failed attempt I've now come up with a working solution. It would appear that jQuery UI selectable is not suitable, as it uses that rectangular selection, so I've had to roll my own.

In the example below (see jsFiddle for full code), the selectable elements are a.date. These elements should have position: relative set.

Firstly, I append a child element with class .drag-proxy. This is positioned absolutely, and covers the same area as the child. It's got no content so it sits transparently above.

These proxy elements are made draggable and droppable.

When the element is dragged and dropped onto another element, it is moved back to its starting point (so that subsequent selections work).

Then we cycle through all the proxy elements, indentifying the elements in the selection range, and then setting a class on the parent a.date element.

$(function() {
  $('a.date').each(function() {
  $(this).append('<a href="' + $(this).attr('href') + '" class="drag-proxy" id="proxy_' + $(this).attr('id') + '"></a>');
  });

  var $currentlyDragged;

  $('.drag-proxy').draggable({
    start: function() {
      $currentlyDragged = $(this);
    },
    // Once drag has finished, return the drag-proxy to its original position
    stop: function() { 
      $(this).css('top', 0).css('left', 0);
      $currentlyDragged = null;
    }
  });

  $('.drag-proxy').droppable({
    over: function(e, ui) {
      var from_id = $currentlyDragged.attr('id');
      var to_id = $(this).attr('id');

      $('.date').removeClass('selected');

      var started = selecting = false;
      $('.drag-proxy').each(function() {
        var $this = $(this);
        var this_id = $this.attr('id');
        if (started === false && (this_id == from_id || this_id == to_id)) {
          selecting = true;
        }
        if (selecting) {
          $this.parent().addClass('selected');
          if (started === true && (this_id == from_id || this_id == to_id)) {
            selecting = false;
          }
        }
        if (started === false && (this_id == from_id || this_id == to_id)) {
          started = true;
        }
      });
    },

    drop: function(e, ui) {
      // do whatever you need to do once selection ends
    }
  });


});
​

See the jsFiddle for the full example.

于 2012-06-24T14:20:17.167 に答える
0

Please note - this answer is incorrect, but I'm leaving it as it shows the extent of what can be done with jQuery UI selectable, and why it doesn't work. See accepted answer for a custom-rolled, working solution.

Thanks to Frédéric Hamidi for the pointer to this forum thread, I have solved this as below. My additional requirements not mentioned in the question, were that the selectable elements have to be in multiple containers (ultimately this will be dates in a calendar, contained in individual months).

Given the following HTML:

<div id="selectable">
  <div>
    <span id="a1">1</span>
    <span id="a2">2</span>
    <span id="a3">3</span>
    <span id="a4">4</span>
    <span id="a5">5</span>
    <span id="a6">6</span>
    <span id="a7">7</span>
    <span id="a8">8</span>
    <span id="a9">9</span>
    <span id="a10">10</span>
  </div>

  <div>
    <span id="b1">1</span>
    <span id="b2">2</span>
    <span id="b3">3</span>
    <span id="b4">4</span>
    <span id="b5">5</span>
    <span id="b6">6</span>
    <span id="b7">7</span>
    <span id="b8">8</span>
    <span id="b9">9</span>
    <span id="b10">10</span>
  </div>
</div>

Then this javascript solves it. Note, the linked post uses slice(start, end) but I can't do that due to my multiple container requirement. So I've looped over all sortable elements with an each(), and toggled selecting based on if we've reached the first and not yet reached the last element.

I set filter: 'span' in order to prevent other child elements of #selectable becoming selectable.

$(function() {
  var rangeSelecting = function(event, ui) {
    var start = $(".ui-selecting", this).first();
    var end = $(".ui-selecting", this).last();
    var selecting = false;
    $(this).find('span').removeClass("range-selecting").each(function() {
      var $this = $(this);
      var this_id = $this.attr('id');
      if (start.attr('id') === this_id) selecting = true;
      if (selecting) $this.addClass('range-selecting');
      if (end.attr('id') === this_id) selecting = false;
    });
  }
  $("#selectable").selectable({
    filter: 'span',
    selecting: rangeSelecting, 
    unselecting: rangeSelecting,
    stop: function(event, ui) { $(".range-selecting", this).addClass("ui-selected").removeClass("range-selecting"); }
  });

});​</p>

See this jsfiddle for a working example.

于 2012-06-23T12:56:08.930 に答える