Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refresh draggable containment

I have a draggable element that has containment set as parent. This works great but the div content can change making it taller. Is there anyway to trigger the drag event that will ensure it doesnt move from inside its parent. As right now it can overlap the parent until it is dragged then the containment kicks in.

Best Regards Mark

like image 818
Mark Noble Avatar asked Jul 19 '10 01:07

Mark Noble


1 Answers

I've implemented a small plugin that does what you want. It can enforce the containment after the draggable changes size and it even works while you're in the middle of a drag. I've tested it with jQuery UI 1.8.6 and jQuery 1.4.3 but it might break with past or future releases since it has to use internal methods.

Working Demo

http://jsbin.com/uvino4/27

Full Source

Plugin

/*
 * jQuery UI RefreshContainment v0.1
 *
 * A plugin for jQuery UI's Draggable. It adds a refreshContainment method to
 * every draggable which allows you to use the containment option on draggables
 * with dynamically changing sizes.
 *
 * Depends:
 *  jquery.ui.core.js
 *  jquery.ui.widget.js
 *  jquery.ui.mouse.js
 *  jquery.ui.draggable.js
 */

(function ($){
  var $window = $(window);

  // We need to know the location of the mouse so that we can use it to
  // refresh the containment at any time.

  $window.data("refreshContainment", {mousePosition: {pageX: 0, pageY: 0}});
  $window.mousemove(function (event) {
    $window.data("refreshContainment", {
      mousePosition: {pageX: event.pageX, pageY: event.pageY}
    });
  });

  // Extend draggable with the proxy pattern.
  var proxied = $.fn.draggable;
  $.fn.draggable = (function (method){
    if (method === "refreshContainment") {
      this.each(function (){
        var inst = $(this).data("draggable");

        // Check if the draggable is already being dragged.
        var isDragging = inst.helper && inst.helper.is(".ui-draggable-dragging");

        // We are going to use the existing _mouseStart method to take care of
        // refreshing the containtment but, since we don't actually intend to
        // emulate a true _mouseStart, we have to avoid any extraneous
        // operations like the drag/drop manager and event triggering.
        // So we save the original member values and replace them with dummies.
        var ddmanager = $.ui.ddmanager;
        $.ui.ddmanager = null;
        var trigger = inst._trigger;
        inst._trigger = function () { return true; }


        var mousePosition = $window.data("refreshContainment").mousePosition;
        var fakeEvent = {
          pageX: mousePosition.pageX, pageY: mousePosition.pageY
        };
        inst._mouseStart(fakeEvent);

        // Return those extraneous members back to the original values.
        inst._trigger = trigger;
        $.ui.ddmanager = ddmanager;

        // Clear the drag, unless it was already being dragged.
        if (!isDragging) {
          inst._clear();
        }
      });
      return this;
    }
    else {
      // Delegate all other calls to the actual draggable implemenation.
      return proxied.apply(this, arguments);
    }
  });
})(jQuery);

Demo HTML

<!DOCTYPE html>
<html>
<head>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.6/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.6/jquery-ui.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>

<style>
  #container { width: 20em; height: 20em; border: 0.5em solid black; }
  .draggable { width: 5em; height: 5em; border: 0.2em solid black; position: absolute; }
  #one { background-color: #F55; }
  #two { background-color: #5F5; }
</style>
</head>
<body>
  <div id="container">
    <div class="draggable" id="one">drag me</div>
    <div class="draggable" id="two">drag me</div>
  </div>
</body>
</html>

Demo JavaScript

var draggables = $('.draggable');

draggables.draggable({containment: 'parent', stack: draggables});

var resizeDraggables = function (){
  draggables.
    each(function (){
      var size = 5 + Math.random() * 5;
      size = size.toString() + "em";
      $(this).css({width: size, height: size});
    }).
    draggable("refreshContainment");
};

resizeDraggables();

setInterval(resizeDraggables, 2000);
like image 177
brianpeiris Avatar answered Sep 28 '22 17:09

brianpeiris