Drag and drop HTML elements onto SVG elements provided by D3

4,618 views
Skip to first unread message

John Williams

unread,
May 30, 2013, 5:06:08 PM5/30/13
to d3...@googlegroups.com
I have a D3 illustration that I want people to be able to change by dragging and dropping HTML elements onto the illustration, and I'm trying to find some way to do it that's as nicely modular as the Jquery UI approach, where you don't have to explicitly define a relationship between the dragged targets and the dropped targets. 

I've been poking at / searching for resources on this all day, and I haven't found much useful. Has anyone here managed to accomplish something like this? 

Christophe Viau

unread,
May 30, 2013, 6:18:53 PM5/30/13
to d3...@googlegroups.com
Why use D3 if JqueryUI suits your needs? Do you want to just replicate
it with D3?
> --
> You received this message because you are subscribed to the Google
> Groups "d3-js" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to d3-js+un...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

John Williams

unread,
May 30, 2013, 7:29:57 PM5/30/13
to d3...@googlegroups.com
The illustration done by D3 is complex, data-backed, and pretty much requires SVG in order to present, so D3 builds and updates that illustration. JQuery and JQueryUI have poor SVG support, and element.droppable() isn't recognized by elements in the SVG dom at all. 

Mohamed Ali Jamaoui

unread,
Sep 10, 2013, 5:23:22 AM9/10/13
to d3...@googlegroups.com
Hi John, 
Have you managed to handle drag and drop with d3.js ?

I actually am creating a similar application where the user can create the d3 graph gradually by drag and drop from a drop down list of measures 
and dimensions. The user interaction trigger the creation of data structure to query the back end and get the data, which then get added to 
visualization. 

The drag and drop behavior has been first implemented using jquery.css function by changing the position of the dragged DOM element, 
however the code and the interaction between components is kind of messy, so I looking for a way to handle the drag and drop using d3. 

but it seems that the d3.behavior.drag works only on elements within an SVG container (coorect me if I am wrong)
because I tried to drag a p element with d3 but it is not working 
here is the code snippet 

var psel  = d3.selectAll("p"); 
console.log(psel); 
psel.style("color","red")
.call(d3.behavior.drag().on("drag", dragHandler).on("dragEnd", dropHandler)); 
var wasMoved = false; 
function dragHandler(d){
console.log('p is dragged');
if(wasMoved){
console.log("dropped"); 
wasMoved = false; 
}else {
console.log("clicked"); 
}
}
function dropHandler(d){
console.log('p is dropped'); 
wasMoved = d3.event.d3 || d3.event.dy; 
if(wasMoved){
d.x += d3.event.dx; 
d.y += d3.event.dy; 
            d3.select(this).attr("transform", "translate(" + d.x + "," + d.y + ")");
            console.log('dragged'); 
}
}

Now I trying to refactor the application using the reusable API pattern, and I am thinking about creating a drag and drop handler module 
where I create custom event using d3.dispatch and manage the drag and drop from the HTML based option palette to the SVG element container. 
How did you managed to handle the drag and drop? 

Thanks & regards 
Mohamed Ali 

John Williams

unread,
Sep 10, 2013, 11:13:51 AM9/10/13
to d3...@googlegroups.com
I abused JQuery UI's "draggable" on the HTML elements, which gave me:
  * "start," "stop," and "drag" events
  * an API for creating helpers

"droppable" doesn't work on SVG, though, so…
 * I created my own drag-drop model object. 
 * I attached "mouseover" and "mouseout" events to the candidate SVG elements

SVG: 
  MouseOver on the SVG element updated the drag-drop model with information on what element the cursor was over
  MouseOut would clear that information

Draggable:
   Start registered the dragged item with the drag-drop model
   Stop used logic in the drop manager to determine if the dragged item and the dropped item were compatible.
      If so, I executed my application logic for what should happen for a successful drop
      If not, I just cleared both the dragged item and the over item from the model.

There were a couple of tricky things:
   * The helper has to be moved out from under the cursor. If your helper is directly under the cursor, mouseOver / mouseOut events won't trigger on the underlying SVG elements (they appear to be intercepted by the helper instead)
   * From the draggable's standpoint, the element always "reverts" ("revert: true"). So on Drag I pre-tested whether or not a drop would be successful, and set the revertDuration to 0 if it was. If it wasn't, I used a revert duration of 300.
 
At no point in this process did I use D3 -- I only used D3 for the drawing, not the drag-drop implementation.

I'll try to write up some sample code if I can claw some time away from other things -- what I have written now is pretty deeply infected with AngularJS.

Mohamed Ali Jamaoui

unread,
Sep 18, 2013, 4:54:01 AM9/18/13
to d3...@googlegroups.com
Hi, 
Nice work, I'd appreciate some code snippet. If you can do a demo an put in the d3 gallery that would be even better for the whole community. 
Thanks & regards 
Mohamed Ali 

John Williams

unread,
Sep 18, 2013, 2:21:48 PM9/18/13
to d3...@googlegroups.com
OK, I knocked together a sample:


I don't object to putting it in the d3 gallery if no one else does. I appreciate any guidance, too, on how to improve the example.

Mohamed Ali Jamaoui

unread,
Sep 19, 2013, 8:43:37 AM9/19/13
to d3...@googlegroups.com
hi John, 
Thanks for the code snippet. A while ago I was trying to create a d3 shape using drag and drop of a dom element to an SVG container. I manged to do this demo using draggable and droppable jquery-ui methods. 

I would like to understand why you are saying that jquery drop won't work at all? 
I would like also to know how would you go about wrapping the drag and drop features in a reusable module following the reusable API architecture. 

 
Thanks & regards 
Mohamed Ali 

John Williams

unread,
Sep 19, 2013, 9:40:52 AM9/19/13
to d3...@googlegroups.com
You are dragging an HTML element into the root SVG element ("svg"). I'm not clear on how this base SVG element fits into the HTML DOM, but for whatever reason it's compatible enough that JQueryUI's "droppable" works on it. I'll clarify that in my gist.

What I was trying to do, however, was use "droppable" on elements within the SVG dom. Those are incompatible with droppable -- at least, incompatible with every element I tried. I played around with your jsfiddle a bit, adding a rect inside the SVG element and attempting to bind droppable to it. Drop never fired. If you can get it to work, do let me know -- I'd love to rip that complexity out of my application.

I'm not sure I understand your second question; which reusable API architecture? I originally abstracted a lot of this out into AngularJS directives.



--
You received this message because you are subscribed to a topic in the Google Groups "d3-js" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/d3-js/XR3D-FJU9uE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to d3-js+un...@googlegroups.com.

Mohamed Ali Jamaoui

unread,
Sep 19, 2013, 11:46:09 AM9/19/13
to d3...@googlegroups.com
hi John, 
I would love to see the results of your playing with the jsfiddle example! 
About the drag and drop within the SVG I think d3 drag behavior would work well if you are trying to drag svg elements (rect, circle ..)
I thought, at least for my case, that the hard part is to drag a DOM element from outside of the SVG container like for instance an li element from a drop down list and create some d3 shape on the drop event. 

I mean by reusable API architecture the pattern described by Mike Bostock to create reusable d3 graphs. He described the pattern here http://bost.ocks.org/mike/chart/. And there is a new book that tries to adopt this approach in real world d3 apps http://bleedingedgepress.com/our-books/developing-a-d3-js-edge/. You can find the code snippets for the book in this gist https://github.com/GerHobbelt/Developing-a-D3.js-Edge

For instance, they are creating a reusable data manger API here https://github.com/GerHobbelt/Developing-a-D3.js-Edge/blob/master/code/chapter-06-12/08.01.drawRoutes/javascripts/transport.js  (called d3edge.dataManager) so I thought why not create a reusable drag and drop handler API that can work easily with d3 visualizations. 

Niket Shah

unread,
Jan 9, 2014, 9:30:07 AM1/9/14
to d3...@googlegroups.com
Hi John,

Thank you very much for the sample code!

Is it possible to see how you abstracted the drag and drop functionality into an AngularJS directive?
Reply all
Reply to author
Forward
0 new messages