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');
});
});