最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - jQuery UI: drag multiple objects - Stack Overflow

programmeradmin0浏览0评论

My scenario : Container (div) with some objects (div). The objects can be moved inside the container (with the option containment set to parent).

Now i need to move multiple objects at once. To do this i found this useful plugin. Unfortunately this plugin does not handle the property containment, as reported here.

My test on JSFiddle , disable this function

$(".obj").on("drag", function(ev, ui)

To active the multiple drag, click on the objects. I was able to block the drag event.

Problem of my test:

At that point i wouldn't know how to reactivate the drag.

Note

I should probably know the direction of the drag (with start - stop events). But at this point i can't stop the drag.

My solutions

But also the K Scandrett solution is very good. It is very difficult to apply in my particular case, which has been simplified in the example.

Always using this plugin for enable the multiple drag. Each time i select multiple objects and drag them, in the dragstart event i do this (change the property containment of the object, depending on the positions of the selected objects) :

//1024 * 648 is the width of the container
$(obj).unbind("dragstart").bind("dragstart" , function(ev, ui){

    var dimObjFirst = {
        x : parseInt($(this).css("left")),
        y : parseInt($(this).css("top"))
    };
    if($("blablabla > div.ui-selected").length > 1){

        var minLeft = 1024,maxRight = 0,minTop = 648,maxDown = 0;

        $("blablabla > div.ui-selected").each(function(){
            var elem = $(this);
            var dim = {
                w : parseInt(elem.css("width")),
                h : parseInt(elem.css("height")),
                l : parseInt(elem.css("left")),
                t : parseInt(elem.css("top")),
            };
            if(dim.l < minLeft) minLeft = dim.l;
            if(dim.l + dim.w > maxRight) maxRight = dim.l + dim.w;
            if(dim.t < minTop) minTop = dim.t;
            if(dim.t + dim.h > maxDown) maxDown = dim.t + dim.h;
        });

        var offsetContainer = $(".container").offset();
        $(this).draggable( "option" , "containment" , [
          (dimObjFirst.x - minLeft) + parseInt(offsetContainer.left),
          (dimObjFirst.y - minTop) + parseInt(offsetContainer.top),
          (dimObjFirst.x + (1024 - maxRight)) + parseInt(offsetContainer.left),
          (dimObjFirst.y) + (648 - maxDown) + parseInt(offsetContainer.top)
        ]);
    }           
});

$(obj).unbind("dragstop").on("dragstop", function(ev, ui) {

    if($("blablabla > div.ui-selected").length > 1) {
        $("blablabla > div.ui-selected").each(function(){
            $(this).draggable( "option" , "containment" , "parent" );
        });
    }
});

And add this line of code this._setContainment(); at the start of the function _mouseDrag of the jQuery UI plugin.

My scenario : Container (div) with some objects (div). The objects can be moved inside the container (with the option containment set to parent).

Now i need to move multiple objects at once. To do this i found this useful plugin. Unfortunately this plugin does not handle the property containment, as reported here.

My test on JSFiddle , disable this function

$(".obj").on("drag", function(ev, ui)

To active the multiple drag, click on the objects. I was able to block the drag event.

Problem of my test:

At that point i wouldn't know how to reactivate the drag.

Note

I should probably know the direction of the drag (with start - stop events). But at this point i can't stop the drag.

My solutions

But also the K Scandrett solution is very good. It is very difficult to apply in my particular case, which has been simplified in the example.

Always using this plugin for enable the multiple drag. Each time i select multiple objects and drag them, in the dragstart event i do this (change the property containment of the object, depending on the positions of the selected objects) :

//1024 * 648 is the width of the container
$(obj).unbind("dragstart").bind("dragstart" , function(ev, ui){

    var dimObjFirst = {
        x : parseInt($(this).css("left")),
        y : parseInt($(this).css("top"))
    };
    if($("blablabla > div.ui-selected").length > 1){

        var minLeft = 1024,maxRight = 0,minTop = 648,maxDown = 0;

        $("blablabla > div.ui-selected").each(function(){
            var elem = $(this);
            var dim = {
                w : parseInt(elem.css("width")),
                h : parseInt(elem.css("height")),
                l : parseInt(elem.css("left")),
                t : parseInt(elem.css("top")),
            };
            if(dim.l < minLeft) minLeft = dim.l;
            if(dim.l + dim.w > maxRight) maxRight = dim.l + dim.w;
            if(dim.t < minTop) minTop = dim.t;
            if(dim.t + dim.h > maxDown) maxDown = dim.t + dim.h;
        });

        var offsetContainer = $(".container").offset();
        $(this).draggable( "option" , "containment" , [
          (dimObjFirst.x - minLeft) + parseInt(offsetContainer.left),
          (dimObjFirst.y - minTop) + parseInt(offsetContainer.top),
          (dimObjFirst.x + (1024 - maxRight)) + parseInt(offsetContainer.left),
          (dimObjFirst.y) + (648 - maxDown) + parseInt(offsetContainer.top)
        ]);
    }           
});

$(obj).unbind("dragstop").on("dragstop", function(ev, ui) {

    if($("blablabla > div.ui-selected").length > 1) {
        $("blablabla > div.ui-selected").each(function(){
            $(this).draggable( "option" , "containment" , "parent" );
        });
    }
});

And add this line of code this._setContainment(); at the start of the function _mouseDrag of the jQuery UI plugin.

Share Improve this question edited Jul 29, 2017 at 10:16 K Scandrett 16.5k4 gold badges42 silver badges68 bronze badges asked Jul 14, 2017 at 12:42 WhiteLineWhiteLine 1,9892 gold badges25 silver badges53 bronze badges 3
  • So are you trying to do this with the plugin or purely in jQuery? I would make a div that contains all the items with draggable, and set a handle for it. This will allow it to contain elements that are draggable and still be dragged. – Twisty Commented Jul 14, 2017 at 15:02
  • With the plugin or with jQuery , is the same. The plugin is a little modification of the original jquery-draggable plugin. Anyway, your proposal is interesting. It's a bit plicated to implement in my case, but it might be a solution. – WhiteLine Commented Jul 17, 2017 at 9:39
  • It would be interesting to natively implement (in the jquery-draggable plugin) this functionality – WhiteLine Commented Jul 17, 2017 at 9:40
Add a ment  | 

2 Answers 2

Reset to default 7 +100

Looks like a fun project so....

I implemented it with a bounding box (similar to Twisty's ment).

I figured the benefit of doing it this way is that it will then constrain all multiple selected objects to the bounds of the container.

The bounding box I've coloured so you can visualise how it works. Of course you'd likely leave it transparent in practice.

Code ments are inline, but happy to answer any questions on the code if you have them.

No plugins were used (just jQuery and jQueryUI).

var disableclick = false;

var boundingBoxTop, boundingBoxBottom, boundingBoxLeft, boundingBoxRight;
var $container = $("#container");
var containerHeight = $container.height();
var containerWidth = $container.width();
var containerTop = $container.offset().top;
var containerLeft = $container.offset().left;

// add the bounding box to the container and make it draggable
var $boundingBox = $("<div id='boundingBox' style='position:absolute;background-color:#fcf5d4'>").prependTo($container);

$boundingBox.draggable({
  grid: [10, 10],
  containment: "parent",
  stop: function( event, ui ) {
    disableclick = true; // don't want to toggle selection when dragging
    setTimeout(function(e){
        disableclick = false;
    },200);
  },
});

$(".obj").click(function(e) {

  if (!disableclick) {

    var $objClicked = $(this);
    $objClicked.toggleClass("ui-selected");

    var $selectedItems = $("#container .ui-selected");

    // move any items in bounding box back into container before we re-process them
    $boundingBox.find('*').each(function() {
      var $this = $(this);

      if ($this.parent().is($boundingBox)) {
        // adjust its positioning to be relative to the container
        $this.css("top", ($this.offset().top - containerTop) + "px");
        $this.css("left", ($this.offset().left - containerLeft) + "px");
        $container.append($this); // return it to the container
      }
    });

    // reversing co-ords to what might be expected here so that we can scale them back to what they need to be for a bounding box
    boundingBoxTop = containerHeight;
    boundingBoxBottom = 0;
    boundingBoxLeft = containerWidth;
    boundingBoxRight = 0;

    // find the bounds of the smallest rectangle that will cover all the currently selected objects
    $selectedItems.each(function() {
      var $this = $(this);

      var top = $this.offset().top - containerTop;
      var bottom = $this.offset().top - containerTop + $this.height();
      var left = $this.offset().left - containerLeft;
      var right = $this.offset().left - containerLeft + $this.width();

      boundingBoxTop = (top < boundingBoxTop) ? top : boundingBoxTop;
      boundingBoxBottom = (bottom > boundingBoxBottom) ? bottom : boundingBoxBottom;
      boundingBoxLeft = (left < boundingBoxLeft) ? left : boundingBoxLeft;
      boundingBoxRight = (right > boundingBoxRight) ? right : boundingBoxRight;
    });

    // get the height and width of bounding box
    var boundingBoxHeight = boundingBoxBottom -= boundingBoxTop;
    var boundingBoxWidth = boundingBoxRight -= boundingBoxLeft;

    if (boundingBoxBottom > 0) // will be negative when nothing is selected
    {
      // update the bounding box with its new position and size
      $boundingBox.css("top", boundingBoxTop + "px");
      $boundingBox.css("left", boundingBoxLeft + "px");
      $boundingBox.css("width", boundingBoxWidth + "px");
      $boundingBox.css("height", boundingBoxHeight + "px");

      // add each selected item to the bounding box so we can drag the box with them in it
      $selectedItems.each(function() {
        var $this = $(this);

        // correct the item's position to be relative to the bounding box
        $this.css("top", ($this.offset().top - containerTop - boundingBoxTop) + "px");
        $this.css("left", ($this.offset().left - containerLeft - boundingBoxLeft) + "px");

        $boundingBox.append($this); // add item to bounding box
      });
    }
  }
});
#container {
  position: absolute;
  width: 400px;
  height: 150px;
  background: #eee;
}

.obj {
  position: absolute;
  background: #ccc;
}

.ui-selected {
  background: #1C90F3;
}

#obj1 {
  width: 20px;
  height: 20px;
  left: 20px;
  top: 20px;
}

#obj2 {
  width: 20px;
  height: 20px;
  left: 100px;
  top: 20px;
}

#obj3 {
  width: 20px;
  height: 20px;
  left: 20px;
  top: 100px;
}

#obj4 {
  width: 20px;
  height: 20px;
  left: 100px;
  top: 100px;
}
<script src="https://code.jquery./jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>

<div style="margin-bottom:10px">
  Click boxes to select/deselect multiple items.<br/>Drag to move selection.
</div>

<div id="container">
  <div class="obj" id="obj1"></div>
  <div class="obj" id="obj2"></div>
  <div class="obj" id="obj3"></div>
  <div class="obj" id="obj4"></div>
</div>

I enhance the answer by using jquery ui Selectable plugin

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>jQuery UI Draggable - Default functionality</title>

    <link rel="stylesheet" href="https://code.jquery./ui/1.12.1/themes/base/jquery-ui.css">

    <style>
        #container {
            position: absolute;
            width: 400px;
            height: 150px;
            background: #eee;
        }

        .obj {
            position: absolute;
            background: #ccc;
        }

        .ui-selected,
        .ui-selecting {
            background: #1C90F3;
        }

        #obj1 {
            width: 20px;
            height: 20px;
            left: 20px;
            top: 20px;
        }

        #obj2 {
            width: 20px;
            height: 20px;
            left: 100px;
            top: 20px;
        }

        #obj3 {
            width: 20px;
            height: 20px;
            left: 20px;
            top: 100px;
        }

        #obj4 {
            width: 20px;
            height: 20px;
            left: 100px;
            top: 100px;
        }
    </style>

    <script src="https://code.jquery./jquery-3.5.1.min.js"></script>
    <script src="https://code.jquery./ui/1.12.0/jquery-ui.min.js"></script>

</head>

<body>

    <div id="container">
        <div class="obj" id="obj1"></div>
        <div class="obj" id="obj2"></div>
        <div class="obj" id="obj3"></div>
        <div class="obj" id="obj4"></div>
    </div>



    <script type="text/javascript">
        var disableclick = false;

        var boundingBoxTop, boundingBoxBottom, boundingBoxLeft, boundingBoxRight;
        var $container = $("#container");
        var containerHeight = $container.height();
        var containerWidth = $container.width();
        var containerTop = $container.offset().top;
        var containerLeft = $container.offset().left;

        // add the bounding box to the container and make it draggable
        var $boundingBox = $("<div id='boundingBox' style='position:absolute;background-color:#fcf5d4'>").prependTo($container);

        $boundingBox.draggable({
            grid: [10, 10],
            containment: "parent",
            stop: function (event, ui) {
                disableclick = true; // don't want to toggle selection when dragging
                setTimeout(function (e) {
                    disableclick = false;
                }, 200);
            },
        });


        $('.obj').draggable({
            grid: [10, 10],
            containment: "parent",
            stop: function (event, ui) {
                disableclick = true; // don't want to toggle selection when dragging
                setTimeout(function (e) {
                    disableclick = false;
                }, 200);
            },
        });

        function selectionStarted() {
            $boundingBox.find('*').each(function () {
                var $this = $(this);

                if ($this.parent().is($boundingBox)) {
                    // adjust its positioning to be relative to the container
                    $this.css("top", ($this.offset().top - containerTop) + "px");
                    $this.css("left", ($this.offset().left - containerLeft) + "px");
                    $this.draggable("enable");
                    $container.append($this); // return it to the container

                }

            });

            $boundingBox.css("top", "0px");
            $boundingBox.css("left", "0px");
            $boundingBox.css("width", "0px");
            $boundingBox.css("height", "0px");
        }

        function selectedEnded() {
            var $selectedItems = $("#container .ui-selected");

            // reversing co-ords to what might be expected here so that we can scale them back to what they need to be for a bounding box
            boundingBoxTop = containerHeight;
            boundingBoxBottom = 0;
            boundingBoxLeft = containerWidth;
            boundingBoxRight = 0;

            // find the bounds of the smallest rectangle that will cover all the currently selected objects
            $selectedItems.each(function () {
                var $this = $(this);

                var top = $this.offset().top - containerTop;
                var bottom = $this.offset().top - containerTop + $this.height();
                var left = $this.offset().left - containerLeft;
                var right = $this.offset().left - containerLeft + $this.width();

                boundingBoxTop = (top < boundingBoxTop) ? top : boundingBoxTop;
                boundingBoxBottom = (bottom > boundingBoxBottom) ? bottom : boundingBoxBottom;
                boundingBoxLeft = (left < boundingBoxLeft) ? left : boundingBoxLeft;
                boundingBoxRight = (right > boundingBoxRight) ? right : boundingBoxRight;
            });

            // get the height and width of bounding box
            var boundingBoxHeight = boundingBoxBottom -= boundingBoxTop;
            var boundingBoxWidth = boundingBoxRight -= boundingBoxLeft;

            if (boundingBoxBottom > 0) // will be negative when nothing is selected
            {
                // update the bounding box with its new position and size
                $boundingBox.css("top", boundingBoxTop + "px");
                $boundingBox.css("left", boundingBoxLeft + "px");
                $boundingBox.css("width", boundingBoxWidth + "px");
                $boundingBox.css("height", boundingBoxHeight + "px");

                // add each selected item to the bounding box so we can drag the box with them in it
                $selectedItems.each(function () {
                    var $this = $(this);

                    // correct the item's position to be relative to the bounding box
                    $this.css("top", ($this.offset().top - containerTop - boundingBoxTop) + "px");
                    $this.css("left", ($this.offset().left - containerLeft - boundingBoxLeft) + "px");

                    $this.draggable("disable");
                    $boundingBox.append($this); // add item to bounding box

                });
            }
        }

    </script>

    <script type="text/javascript">
        $("#container").selectable({
            start: selectionStarted,
            stop: selectedEnded
        });
    </script>

</body>

</html>

发布评论

评论列表(0)

  1. 暂无评论