Draggable nested blocks

Dynamically switching on the 'draggable' attribute.

by Ben Clayton

HTML

You can drag the blocks by thier titlebars. But the text inside is still drag selectable.
<div class="dropmarker"></div>
<div class="block">
  <div class="titlebar"></div>

  <div class="content group">
    <div class="dropmarker"></div>
    <div class="block">
      <div class="titlebar"></div>

      <div class="content">
        <textarea>drag selectable text</textarea>
      </div>

    </div>
    <div class="dropmarker"></div>
    <div class="block">
      <div class="titlebar"></div>

      <div class="content">
        <textarea>drag selectable text</textarea>
      </div>

    </div>
    <div class="dropmarker"></div>

  </div>

</div>

<div class="dropmarker"></div>

<div class="block">
  <div class="titlebar"></div>

  <div class="content group">
    <div class="dropmarker"></div>
    <div class="block">
      <div class="titlebar"></div>

      <div class="content">
        <textarea>drag selectable text</textarea>
      </div>

    </div>
    <div class="dropmarker"></div>
    <div class="block">
      <div class="titlebar"></div>

      <div class="content">
        <textarea>drag selectable text</textarea>
      </div>

    </div>
    <div class="dropmarker"></div>


  </div>

</div>
<div class="dropmarker"></div>

CSS

.block {
  display: block;
  background-color: rgba(240, 255, 255, 0.6);
  margin-bottom: 10px;
  border: 1px solid red;
  margin-top: 10px;
}

.block.dragging {
  opacity: 0.2;
}

.titlebar {
  display: block;
  width: 100%;
  height: 20px;
  background-color: rgba(100, 150, 200, 0.5);
  border-radius: 3px;
  cursor: move;
}
.content {
  padding: 10px;
}

.dropmarker {
  height: 10px;
  position: relative;
}
.dropmarker::before {
  content: ' ';
  position: absolute;
  opacity: 0;
  display: block;
  background-color: rgb(255, 255, 255);
  border-radius: 8px;
  height: 0px;
  width: 100%;
  top: 5px;
  transition: all 0.5s;
}

/* I would like to exclude dropping inside self .dropbox:not(.dragging) */
.dragging-block .dropmarker::before {
  opacity: 0.6;
  background-color: rgb(100, 255, 200);
  height: 20px;
  top: -5px;
}

.dragging-block.dragging-found .dropmarker::before {
  opacity: 0;
}

.dragging-file .dropmarker::before {
  opacity: 0.6;
  background-color: rgb(255, 100, 200);
  height: 20px;
  top: -5px;
}


.dragging-block.dragging-found .dropmarker.dragover::before{
  opacity: 0.8;
  background-color: rgb(100, 255, 200);
  height: 30px;
  top: -10px;
}

textarea {
  width: 100%;
  height: 100%;
}


/* don't highlight inside the block being dragged, and try also to hide adjacent siblings */
body.dragging-block .dragging + .dropmarker::before,
body.dragging-block .dragging .dropmarker::before {
  opacity: 0;
}

JavaScript

$(document).ready(function() {

  $(document).on('mousedown', '.titlebar', function() {
    var $p = $(this).parent();
    $p.attr("draggable", true);
    $p.addClass('dragging');
  });
  $(document).on('mouseup', ".titlebar", function() {
    var $p = $(this).parent();
    $p.attr("draggable", false);
    $p.removeClass('dragging');
  });


  $('body').on('dragenter dragstart', function(e) {
    console.log("START", e.originalEvent.target.classList);
    //e.preventDefault();

    if ( e.originalEvent.target.classList ) {
      $(this).addClass('dragging-block');
    } else {
      $(this).addClass('dragging-file'); // external object
    }
  });

$('body').on('dragleave', function(e) {
    console.log("LEAVE", e.originalEvent.target.classList);
    //e.preventDefault();
		$(this).addClass('dragging-found');
  });

  $('body').on('dragend', function(e) {
    console.log("END", e.originalEvent.target.classList);

    $('body')[0].className ="";// bit brutal only wanted to remove classnames starting in  "dragging-"

  });

  

  $(document).on('dragstart', '.block', function(e) {
    e.stopPropagation();
    $(this).addClass('dragging');
  });
  $(document).on('dragend', '.block', function() {
    var $blk = $(this);
    $blk.attr("draggable", false);
    $blk.removeClass('dragging');
  });


  $(document).on('dragenter', '.dropmarker', function(e) {
    e.stopPropagation();
    $(this).addClass('dragover');
  });
  $(document).on('dragleave', '.dropmarker', function(e) {
    e.stopPropagation();
    $(this).removeClass('dragover');
  });

});