10

I have a container with a list inside. The list items can be dragged, moving with the mouse.

The container is scrollable with:

overflow-y: scroll;

By setting this property, Chrome automatically sets the overflow-x property to 'auto'. If I set overflow-x: visible it is ignored by Chrome. If I set overflow-x: hidden then obviously the item is cropped.

When I drag a list item outside of the left or top edge of the container, it is cropped to the edges of the container. If I drag it out of the right or bottom edges the container scrolls to accommodate it. I would like the item to be able to dragged outside of the container without it being cropped and without it triggering scroll.

Given that the container must be set to overflow-y: scroll and that this in turn forces Chrome to set overflow-x: auto, is there any way I can achieve this or is it impossible?

Codepen: http://codepen.io/Pedr/pen/azLWeY

Note: I know I can hack this by using padding to offset the container (so that the limits of the container actually end beyond its visual edges), but that is not an option in my situation.

$(function() {
  $('.Wrapper').mousemove(function(event){
    $('.Item').offset({left: event.pageX, top: event.pageY});
  });
})
html,
body {
  height: 100%;
}

.Wrapper {
  width: 100%;
  height: 100%;
  position: absolute;
}

.Container {
  background: grey;
  position: absolute;
  width: 50%;
  left: 25%;
  min-height: 100%;
  overflow-y: scroll;
  overflow-x: hidden; // Clips Item
  // If left at auto it will clip the item on the top and left edge and scroll if the item overlaps the bottom or right edge.
}

.Item {
  padding: 20px;
  background: red;
  position: absolute;
  width: 600px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="Wrapper">
  <div class="Container">
    <div class="Item">ITEM</div>
  </div>
</div>
yunzen
  • 32,854
  • 11
  • 73
  • 106
Undistraction
  • 42,754
  • 56
  • 195
  • 331
  • 7
    Haha, that title caught my attention! "Dragging a child out of a container"... what?? Oh, that... – Wouter Thielen Feb 12 '15 at 01:48
  • For the record, I don't believe you're supposed to downvote every answer that is not what you're looking for. – deebs Feb 12 '15 at 14:07
  • @deebs I just downvote every answer which took zero effort to write. – Undistraction Feb 13 '15 at 21:18
  • If there's an answer, then 'zero effort' is impossible. I guess you could say 'minimal effort'... that is more understandable. Either way, that fact that you can determine the effort that each person made is pretty amazing. – deebs Feb 16 '15 at 16:58
  • @deeps I must have really upset you for you to come back and edit your comment a day later. This isn't a popularity contest. If an answer shows little effort (and I consider solving a CSS issue like this with JS involves very little effort) or doesn't solve the question in a way I'm happy with then I mark it down because I didn't consider it useful. – Undistraction Feb 16 '15 at 18:15

7 Answers7

5

Set the ITEM to position fixed.... to move it away from everything else

It Works like this

$(function() {
  $('.Wrapper').mousemove(function(event){
      $('.Item').css('position', 'fixed');
      $('.Item').offset({left: event.pageX, top: event.pageY});
    });
})

Look on the snippet here:

$(function() {
  $('.Wrapper').mousemove(function(event){
      $('.Item').css('position', 'fixed');
      $('.Item').offset({left: event.pageX, top: event.pageY});
    });
})
.Wrapper {
  width: 100%;
  height: 100%;
  position: absolute;
}

.Container {
  background: grey;
  position: absolute;
  width: 50%;
  left: 25%;
  min-height: 100%;
  overflow-y: scroll;
  overflow-x: hidden; // Clips Item
  // If left at auto it will clip the item on the top and left edge and scroll if the item overlaps the bottom or right edge.
}

.Item {
  padding: 20px;
  background: red;
  position: absolute;
  width: 600px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="Wrapper">
  <div class="Container">
    <div class="Item">ITEM</div>
  </div>
</div>
Federico
  • 1,231
  • 9
  • 13
3

Here is my edit: http://codepen.io/anon/pen/MYrxGJ

What I did is, set the Wrapper's position to relative, remove the Container's position CSS, and set it to margin-left: 25%; margin-right: 25%; (same effect as margin: 0px auto;). This way, the absolute positioning of the Item div is relative to the Wrapper div, instead of the Container div.

Wouter Thielen
  • 1,016
  • 9
  • 21
1

I believe this takes care of your overflow-y issue. Basically, "turn off" overflow-y on .mousemove() and set the item element outside of the container initially. And you could remove the position: absolute from the container, and have this: http://codepen.io/anon/pen/jEaRaz

$(function() {
  $('.Wrapper').mousemove(function(event){
  $('.Item').offset({left: event.pageX, top: event.pageY});
  $('.Container').css('overflow-y', 'hidden')
  });
})
deebs
  • 1,390
  • 10
  • 23
1

Personally I'd use position:fixed - it would pull the draggable out of the render tree for the container, so as far as the container is concerned it will have nothing to do with the overflow and will simply draw directly on the window without any scrollbars or clipping.

$('.Wrapper').on("mousedown", ".Item", function(event){
    $(event.target)
        .css('position', 'fixed')
        .on("mousemove", function(event){
            // Take into account offset within element
            $(this).offset({left: event.pageX, top: event.pageY});
        })
        .one("mouseup", function(event){
            // Finish dealing with element and clear position and event
            $(this)
                .off("mousemove")
                .css('position', '');
        })
});
Rycochet
  • 2,860
  • 1
  • 22
  • 39
-1

You could duplicate the item outside of the Container when the Item is selected, and then remove it after it's dropped.

http://codepen.io/anon/pen/OPOMMp

Also, I'd hide the overflow on the wrapper.

.Wrapper {
  width: 100%;
  height: 100%;
  position: absolute;
  overflow: hidden;
}
syntacular
  • 52
  • 4
  • Whilst that would solve the problem, it is also avoiding it. Unfortunately I'm bound by the limits of a JS component which doesn't allow for the dragged item to exist outside of the originating container. – Undistraction Feb 05 '15 at 17:54
-1

I figured this could be done by simply making most of your elements in-layout and making the draggable items "out-of-layout" and it turns out I was correct. If you remove the position: absolute from your wrapper and from your container (making them render in-layout) and then give the container margin-left: 25%, you maintain the same effect as before but, since the item itself is position: absolute with no relatively or absolutely positioned parent elements, it is positioned "out-of-layout" which means it can come out of that container. See this example: http://codepen.io/anon/pen/vEWGaa

(SO wants me to have some code with a codepen link so here is the new CSS):

html,
body {
  height: 100%;
}

.Wrapper {
  width: 100%;
  height: 100%;
}

.Container {
  background: grey;
  width: 50%;
  margin-left: 25%;
  min-height: 100%;
  overflow-y: scroll;
  overflow-x: hidden; // Clips Item
  // If left at auto it will clip the item on the top and left edge and scroll if the item overlaps the bottom or right edge.
}

.Item {
  padding: 20px;
  background: red;
  position: absolute;
  width: 600px;
}

EDIT: Also, if you put overflow: hidden on the body, you keep from getting scrollbars when your item being dragged goes off-screen.

Joshua Whitley
  • 1,196
  • 7
  • 21
-1

Try this way, I have cloned the draggable item and append to body.

JS

var cloneItem = $('.Item').clone().hide().appendTo("body");

http://codepen.io/nandhakumaru/pen/VYyMqr

Nandhakumar
  • 366
  • 2
  • 9