Skip to content

Instantly share code, notes, and snippets.

@billdwhite
Created January 26, 2018 17:09
Show Gist options
  • Save billdwhite/6354991ab281192449c298cd52ff1017 to your computer and use it in GitHub Desktop.
Save billdwhite/6354991ab281192449c298cd52ff1017 to your computer and use it in GitHub Desktop.
Drag from HTML, Drop to SVG
license: mit

This demonstrates dragging from HTML to SVG elements, which can be tricky because of the differences between SVG and HTML DOMs. JQueryUI has a nice API for handling drag and drop, including defining the appearance of a dragged object and providing start, drag, and stop events. Unfortunately, JQueryUI's "drop" does not work at all on SVG elements inside the SVG tag, although it does appear to work on the SVG tag itself.

To get over this limitation, we create a custom Drag/Drop manager, and use the start, drag, and stop events for the dragged elements in conjunction with mouseover and mouseout events on the SVG elements to determine whether or not a "drop" has been dropped on a legal SVG element.

forked from thudfactor's block: Drag from HTML, Drop to SVG

<!DOCTYPE html>
<html>
<head>
<title>D3 Drag Drop Example</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://codeorigin.jquery.com/jquery-1.10.2.min.js"></script>
<script src="http://codeorigin.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
<style>
body {
height: 100%;
text-align: center;
padding: 1rem;
}
.col {
display: inline-block;
width: 200px;
vertical-align: top;
}
ul {
margin: 0; padding: 0;
}
li {
margin: 0 0 .3rem 0;
padding: .3rem;
list-style: none;
background-color: #fff;
background-color: rgba(255,255,255,.8);
font: .8rem/1 arial, helvetica, sans-serif;
border: 1px #7f7f7f solid;
}
li.ui-draggable {
cursor: move;
}
li.ui-state-disabled {
cursor: not-allowed;
opacity: .5;
}
rect {
stroke: #7f7f7f;
stroke-width: 1
}
text {
font-family: arial, helvetica, sans-serif;
font-size: 20px;
fill: #fff;
}
.color1 { fill: #1f77b4; }
.color2 { fill: #2ca02c; }
.color3 { fill: #9467bd; }
</style>
</head>
<body>
<div class="col droptargets"></div>
<div class="col draggables"></div>
<script>
// --
// Setup: Creating the dataset and placing the data in the document.
// --
// Establish our dataset
var dwarfSet = {
Fairytale : ["Blick","Flick","Glick","Quee"],
Tolkien : ["Thorin","Gloin","Fili","Kili"],
Pratchett : ["Carrot","Goodmountain","Littlebottom"]
}
var types = d3.keys(dwarfSet);
var dwarves = d3.shuffle(d3.merge(d3.values(dwarfSet)));
// Droppable items on the right
var draggables = d3.select(".draggables").append("ul");
draggables.selectAll("li").data(dwarves).enter()
.append("li")
.text(function(d) { return d })
// Drop targets on the left
var canvas = d3.select(".droptargets").append("svg")
.attr("width",200)
.attr("height",400)
.attr("class","YlGn");
var dropTargets = canvas.selectAll("rect").data(types).enter().append("g")
dropTargets.append("rect")
.attr({
width: 180,
height: 100,
x: 10.5,
y: function(d,i) { return (i * 110) + .5},
rx: 20,
ry: 20,
class: function(d,i) { return "color" + (i+1) }
})
dropTargets.append("text")
.text(function (d) { return d })
.attr({
x: 25.5,
dy: 30,
y: function(d,i) { return (i * 110) + .5}
});
// ---
// Handle dragging from HTML to dropping on SVG
// ---
var DragDropManager = {
dragged: null,
droppable: null,
draggedMatchesTarget: function() {
if (!this.droppable) return false;
return (dwarfSet[this.droppable].indexOf(this.dragged) >= 0);
}
}
var body = d3.select("body");
// Register the associated element as a potential target on mouseOver
// D3 events call listeners with the current datum
// and index, so this is straightforward.
dropTargets.on('mouseover',function(d,i){
DragDropManager.droppable = d;
});
// Clear the target from the DragDropManager on mouseOut.
dropTargets.on('mouseOut',function(e){
DragDropManager.droppable = null;
});
// Set up jqueryUI's draggable on the list items
//
// Note that we have to move helper out of the way of the cursor
// using the "cursorAt" property; otherwise the cursor will be
// located over the helper and the SVG element's mouseover/mouseout
// events will not fire.
$(".draggables li").draggable({
revert: true,
revertDuration: 200,
cursorAt: { left: -2, top: -2 },
// Register what we're dragging with the drop manager
start: function (e) {
// Getting the datum from the standard event target requires more work.
DragDropManager.dragged = d3.select(e.target).datum();
},
// Set cursors based on matches, prepare for a drop
drag: function (e) {
matches = DragDropManager.draggedMatchesTarget();
body.style("cursor",function() {
return (matches) ? "copy" : "move";
});
// Eliminate the animation on revert for matches.
// We have to set the revert duration here instead of "stop"
// in order to have the change take effect.
$(e.target).draggable("option","revertDuration",(matches) ? 0 : 200)
},
// Handle the end state. For this example, disable correct drops
// then reset the standard cursor.
stop: function (e,ui) {
// Dropped on a non-matching target.
if (!DragDropManager.draggedMatchesTarget()) return;
$(e.target).draggable("disable");
$("body").css("cursor","");
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment