Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drag a "nephew" draggable to an "aunt" droppable while maintaining CSS and dimensions

I want to drag a draggable #red, nested within some parent, to some droppable #green, which sits a level higher in the div structure.

The dimensions of the divs need to be expressed in percent (to be responsive), and the toplevel divs must be position: relative;, which might make things more complicated.

For comparison, this all works fine for a the simpler case, where draggable #blue and droppable #green are sibling elements, but breaks in the "nephew" scenario (#red on #green).

I understand that I need to:

  • helper: clone the nephew (#red) div, as well as to appendTo: "body", so that I the nephew element can "escape"
  • I also get that I need to append some styles (especially position: relative;) to .ui-draggable-dragging, so that the nephew element's dimensions are calculated with reference to the parent, while in transit.

So far so good.

One wrinkle remains: When the nephew is dragged, it jumps sideways, and can't ever be dropped.

What's going on here?

  $(function() {
    $("#blue").draggable({
      snap: ".hexagon",
      snapMode: "inner",
      snapTolerance: 20,
      opacity: 0.7,
      addClasses: true,
      // stack: ".item",
      revert: "invalid"
    });
    $("#red").draggable({
      snap: ".hexagon",
      snapMode: "inner",
      snapTolerance: 20,
      opacity: 0.7,
      addClasses: true,
      // stack: ".item",
      revert: "invalid",
      appendTo: "body",
      helper: "clone",
    });
    $("#green").droppable({
      accept: ".hexagon",
      tolerance: "fit",
      drop: function(event, ui) {
        $(this)
          .addClass("ui-state-highlight")
          .find("span")
          .html("Dropped!");
      }
    });
  });
.hexagon {
    width: 20%;
    padding-top: 25%;
    overflow: hidden;
    -webkit-clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
    clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
    -webkit-shape-outside: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
    float: left;
    position: relative;
    z-index: 1;
  }

  .outer {
    position: relative;
    z-index: 0;
  }

  .inner {
    background-color: red;
    z-index: 9999;
    position: absolute;
    margin: 0 auto;
    width: 100%;
    top: 0;
    bottom: 0;
  }

  .textstyle {
    color: white;
    font-family: sans-serif;
    top: 25%;
    left: 10%;
    right: 10%;
    bottom: 10%;
    text-align: center;
    position: absolute;
    font-size: 1vw;
  }

  .green {
    background-color: green;
    z-index: 1000;
  }

  .blue {
    background-color: blue;
  }

  .red {
    background-color: red;
  }

  .ui-draggable-dragging {
    position: relative;
    padding-top: 25%;
    width: 20%;
  }
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div id="green" class="hexagon outer green">
    <span class="textstyle">
            I am the dropzone.
          </span>
  </div>
  <div id="blue" class="hexagon outer blue">
    <span class="textstyle">
            I drag just fine, because I'm just a sibling element.
    </span>
  </div>
  <div class="hexagon outer">
    <div id="red" class="hexagon inner red">
      <span class="textstyle">
            I am nested, and I make a mess when dragged.
            I am the nephew.
      </span>
    </div>
  </div>
like image 810
maxheld Avatar asked May 01 '17 22:05

maxheld


1 Answers

Why does the clone jump?

It appears that the jQuery draggable plugin is adding a left value to the clone on the proviso that it is being positioned absolutely, by forcing .ui-draggable-dragging to position: relative; the plugin is actually positioning the clone left relative to where the element was positioned initially on the page.

To fix this behaviour add an override to the clone to position it absolutely:

.red.ui-draggable-dragging {
    position: absolute;
    bottom: auto;
}

Additionally, add bottom: auto; to override the bottom value set by .inner as this will cause the clone to stretch.

Why can't the clone be dropped?

This is because of tolerance: "fit" which according to the documentation:

"fit": Draggable overlaps the droppable entirely.

Droppable Widget - (https://api.jqueryui.com/droppable/#option-tolerance)

This still causes problems now that the clone is positioned absolutely as it's width is not calculated to be the same as the original .hexagon (the original's width: 20%; is calculated against the body width while the clone's width: 20%; is calculated against the viewport). To mitigate this the default browser margin on the body has been suppressed (although this could also be solved by wrapping the .hexagons in a container).

If the dragging action does not need to be exact tolerance: "fit" can be changed to tolerance: "intersect" which will allow for a bit more leeway.

$(function() {
  $("#blue").draggable({
    snap: ".hexagon",
    snapMode: "inner",
    snapTolerance: 20,
    opacity: 0.7,
    addClasses: true,
    // stack: ".item",
    revert: "invalid"
  });
  $("#red").draggable({
    snap: ".hexagon",
    snapMode: "inner",
    snapTolerance: 20,
    opacity: 0.7,
    addClasses: true,
    // stack: ".item",
    revert: "invalid",
    appendTo: "body",
    helper: "clone"
  });
  $("#green").droppable({
    accept: ".hexagon",
    tolerance: "fit",
    drop: function(event, ui) {
      $(this)
        .addClass("ui-state-highlight")
        .find("span")
        .html("Dropped!");
    }
  });
});
body {
  margin: 0;
}

.hexagon {
  width: 20%;
  padding-top: 25%;
  overflow: hidden;
  -webkit-clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  -webkit-shape-outside: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  float: left;
  position: relative;
  z-index: 1;
}

.outer {
  position: relative;
  z-index: 0;
}

.inner {
  background-color: red;
  z-index: 9999;
  position: absolute;
  margin: 0 auto;
  width: 100%;
  top: 0;
  bottom: 0;
}

.textstyle {
  color: white;
  font-family: sans-serif;
  top: 25%;
  left: 10%;
  right: 10%;
  bottom: 10%;
  text-align: center;
  position: absolute;
  font-size: 1vw;
}

.green {
  background-color: green;
  z-index: 1000;
}

.blue {
  background-color: blue;
}

.red {
  background-color: red;
}

.ui-draggable-dragging {
  position: relative;
  padding-top: 25%;
  width: 20%;
}

.red.ui-draggable-dragging {
  position: absolute;
  bottom: auto;
}
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div id="green" class="hexagon outer green">
  <span class="textstyle">
            I am the dropzone.
          </span>
</div>
<div id="blue" class="hexagon outer blue">
  <span class="textstyle">
            I drag just fine, because I'm just a sibling element.
    </span>
</div>
<div class="hexagon outer">
  <div id="red" class="hexagon inner red">
    <span class="textstyle">
            I am nested, and I make a mess when dragged.
            I am the nephew.
      </span>
  </div>
</div>
like image 122
Hidden Hobbes Avatar answered Oct 14 '22 15:10

Hidden Hobbes