JQuery UI draggable: Exceed containment on one side
Asked Answered
A

4

7

I am using JQuery UI to implement resizable/draggable elements. Now I would like to define a containment for these elements that limits the resizing/dragging on exactly three(!) sides. E.g. have a look at this JSFiddle example. You can see that the contained element can only be resized/dragged inside the parent's area.

What I would like to achieve is that the element can exceed the bottom threshold and be moved to beyond the bottom border of the parent. Nevertheless the resizing/dragging should still be limited at the top, right and left sides as is prescribed by the parent's according borders.

So this modification is what I came up with:

// resizable/draggable option
resize/drag : function(event, ui) {
    expandParent("#parentElement", "#dragElement");
}

function expandParent(parentName, childName) {
    var dragEl = $(childName);
    var dragElTop = parseInt(dragEl.css("top"), 10);
    var dragElHeight = parseInt(dragEl.css("height"), 10);
    var dragElBottom = dragElTop + dragElHeight;
    var parentEl = $(parentName);
    var parentElBottom = parseInt(parentEl.css("height"));
    if(parentElBottom <= dragElBottom + 20) {
        parentEl.css("height", "+=2");
    }
}

If you play around with the bottom border you notice that the parent's area is expanded if the child gets too close to the bottom border of the parent. Unfortunately this stops after 20 pixels. You then have to release the mouse button and resize/drag again to extend the area further. Do you have any idea why that is?

Assurance answered 13/7, 2013 at 11:44 Comment(2)
Without having tested your code or anything, I noticed the following line: if(parentElBottom <= dragElBottom + 20) { maybe that 20 is causing the problem?Amino
Well, yes, you can move the element to the lowest possible position that was part of the parent's area at the beginning of the movement. So the extension of the area is somehow not forwarded to JQuery UI's model I guess.Assurance
R
5

Well that expandParent function is not going to work because container boundaries has been set by JQuery UI and it wont be updated until you release the mouse .

So i can think of two solution here one is elegant and the other is tricky

Obviously the elegant solution would be writing your own containing method which is free at bottom .

But now i have a tricky solution by using a dummy parent element and set its height to a large value like 1000 or height of window and then set overflow hidden attribute for real parent.

Here is my solution :

<div id="parentElement">
    <div id="dummy-parent">
        <div id="dragElement" class="dragClass">
            <p>Drag me around!</p>
        </div>
    </div>
</div>



function expandParent(parentName, childName) {
    var dragEl = $(childName);
    var parentEl = $(parentName);
    var dragElHeight=dragEl.height();
    if(parentEl.height() <= dragElHeight) {
        parentEl.height(dragElHeight);
    }
}

Check the JS Fiddle

You have to modify the code based on your application i just gave you the idea

Remde answered 15/7, 2013 at 18:50 Comment(2)
The tricky solution seems to be smart! So there is no way to manipulate the values that JQuery UI is using? I guess they are set in some container somewhere so maybe one can act on that.Assurance
Is this still the best solution for this particular use case? I'm looking into doing the same thing and want to make sure there hasn't been any improvements in this area recently.Odonto
M
2

Looks like inside JQuery UI they keep the information of the parent at the beginning of the drag, so even if you change the size it will still restrict the drag.

To fix that you can set your own containment bounding box.

$("#dragElement")
    .draggable({
        cursor      : "move",
        scroll      : false,
        containment : [20, 20, 400+20, 1000],
        drag : function(event, ui) {
            expandParent("#parentElement", "#dragElement");
        }
    })
    .resizable({
        containment : "parent",
        resize : function(event, ui) {
            expandParent("#parentElement", "#dragElement");
        }
    });

function expandParent(parentName, childName) {
    var dragEl = $(childName);
    var dragElTop = parseInt(dragEl.css("top"), 10);
    var dragElHeight = parseInt(dragEl.css("height"), 10);
    var dragElBottom = dragElTop + dragElHeight;
    var parentEl = $(parentName);
    var parentElBottom = parseInt(parentEl.css("height"));
    if(parentElBottom <= dragElBottom + 20) {
        parentEl.css("height", dragElBottom+20);
    }
}

With the above code, it will allow the resize of the parent up to 1000+20 in height. You can try the above code here (JSFiddle).

If needed to do a dynamic container, at the beginning (and in the resize method), set the new container with the correct BoundingBox.

var p = $("#parentElement");
var dragWidth = parseInt($("#dragElement").css('width'));
var left = p[0].offsetLeft;
var top = p[0].offsetTop;
var width = parseInt(p.css("width"), 10)-dragWidth;
var containment = [left, top, width+left, 1000];

Yeah, I know the solution is kind of hackish.. but anyway, I just updated the JSFiddleThis solution may not be to calculate and re-set dynamically the size of the container. Just wanted to have a JSFiddle working nicely.

Minetta answered 15/7, 2013 at 17:52 Comment(4)
Ok, this is an approach that I did not think of, yet. But it is limited to absolute values, isn't it? I would like to have the containment be relative to the parent. Manual adaption of the bounding box's position to the position of the parent seems to be quite inconvienient and verbose.Assurance
I added the code to calculate the bounding box of the containment.Minetta
I just updated JSFiddle with the solution working correctly and dynamically. Just wanted to have a working version of the solution there.Minetta
That is good! But I kind of prefer the solution of codedme due to its simplicity - although it does not fulfill the demands completely it covers most use cases.Assurance
B
0
    $("#dragElement")
        .draggable({
            cursor      : "move",
            containment : '#Container',
            drag : function(event, ui) {
                var growAmount = 10;
                var cont = $('#Container');
                var item = $(ui.helper);

                var itemOffset = item.offset();
                itemOffset.bottom = itemOffset.top + dProps.item.height();


                var contOffset= cont.offset();
                contOffset.bottom = contOffset.top + cont.height();

                if(itemOffset.bottom >= contOffset.bottom){
                    var inst = item.draggable("instance");
                    inst.containment = [inst.containment[0], inst.containment[1], inst.containment[2], inst.containment[3] + growAmount];
                    cont.height(parseInt(cont.height()) + growAmount);
                }
            }
        });
Burley answered 14/8, 2014 at 16:3 Comment(0)
R
0

Ok, so this is possible, but not without shinanigans...

Basically the containment array parameter accepted by jQuery's draggable, will only constrain to the values provided. Normally, the array relates to [x1, y1, x2, y2], so if we only provide the first 3, and leave the 4th undefined, then the bottom will not be constrained.

However, this means that we need to manually provide the left, top, and right values. Top and left are easy enough, but right must be calculated:

var scrollbarWidth = 22;
var container = $('#my-container');
var cOffset = container.offset();
var cRight = cOffset.left + c.outerWidth() - $('#my-draggable').outerWidth() - scrollbarWidth; 

$('#my-draggable').draggable({
  containment: [ cOffset.left, cOffset.top, cRight ],
  scroll: true
});

This will work fine to start with, BUT... and here's the kicker, every time the container moves, the draggable element will have to have it's containment attribute updated. Additionally, this can also be an issue if the window is resized. That said, I do have a fully functioning example here.

Riding answered 19/3, 2019 at 2:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.