0
votes

I want to show multiple charts in a table, each chart is a separate svg, generated with d3.

The height of each chart has to be easily adjustable - and I want that to be done in a similar manner as you adjust the 4 panels (html, javaScript, css, output) in jsFiddle, by dragging the mouse over a border between those panels - in my case it's only horizontal borders.

I'd like to use d3.behavior.drag(). When the border (a div in a td of the table, or something like that) is dragged, the height of some of the svg elements has to change. In the code I got so far, I'm putting simple div's rather than svg's, to keep things simple.

I got 2 versions; one that "should" be working, but isn't, and one the is working, but I don't like it (it uses d3.mouse(), which "should" be unnecessarily complicated); I would like to find out whether I found a bug in d3, or (more likely), I'm not doing it right in the version that "should" be working.

Voilà the working version; to see the not working version (in which "jittering" occurs), call _dragmove instead of dragmove (where it says .on("drag"...))

var heights, xElt, yElt, dxElt, dyElt, datumXElt, datumYElt, mouseElt, bodyMouseElt, theBody, drag, spacers;

    heights = d3.selectAll(".content")[0].map(function(elt){return parseInt(d3.select(elt).style("height"));});

    xElt         = d3.select("#xLog");
    yElt         = d3.select("#yLog");
    dxElt        = d3.select("#dxLog");
    dyElt        = d3.select("#dyLog");
    datumXElt    = d3.select("#datumXLog");
    datumYElt    = d3.select("#datumYLog");
    mouseElt    = d3.select("#mouseLog");
    bodyMouseElt    = d3.select("#bodyMouseLog");
    theBody = d3.select("body").node();

    drag = d3.behavior.drag()
        .origin(function(x){return x;})
        .on("drag", dragmove)
        .on("dragstart", function(d){
            console.log("start" + d.index);
            y0 = d3.mouse(theBody)[1];
        })
        .on("dragend", function(d){
            console.log("end " + d.index);
            var newHeight = parseFloat(d3.select("#content_" + d.index).style("height"));
            heights[d.index] = newHeight;
        });

    spacers = d3.selectAll("div.spacer");
    spacers.data(d3.range(heights.length).map(function(d,i){ return {x:0, y:0, index: i }}));

    spacers.call(drag);  
    
    
    function showDebugOutput(d, that){
    xElt.text(d3.event.x);
    yElt.text(d3.event.y);
    dxElt.text(d3.event.dx);
    dyElt.text(d3.event.dy);
    datumXElt.text(d.x);
    datumYElt.text(d.y);
    mouseElt.text(d3.mouse(that).join(", "));
    bodyMouseElt.text(d3.mouse(theBody).join(", "));
}
//_NOT_WORKING
function _dragmove(d){
    showDebugOutput(d, this);
    var elt = d3.select("#content_" + d.index);
    var originalHeight = heights[d.index];
    var newHeight = originalHeight + d3.event.y;
    elt.style("height",  newHeight + "px");
    d.x = d3.event.x;
    d.y = d3.event.y;
    elt.html("<br />" + newHeight);
}
function dragmove(d){
    showDebugOutput(d, this);
    var elt = d3.select("#content_" + d.index);
    var originalHeight = heights[d.index];
    y1 = d3.mouse(theBody)[1];
    var dy = y1 - y0;
    var newHeight = originalHeight + dy;
    elt.style("height",  newHeight + "px");
    d.x = d3.event.x;
    d.y = dy;
    elt.html("<br />" + newHeight);
}
		div.spacer {
		    width: 400px;
		    height:  5px;
		    background-color: #ddd;
		    cursor: ns-resize;
		    position: relative;
		    left: 0;
		    top: 0;
		}
		div.content {
		    width: 400px;
		}
		div.content.first {
			background-color: yellow;
			height: 100px;
		}
		div.content.second {
			background-color: green;
			height: 200px;
		}
		div.content.third {
			background-color: orange;
			height: 70px;
		}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

	<table cellspacing="0" cellpadding="0">
	    <tr><td><div class="content first" id="content_0"></div></td></tr>
	    <tr><td><div class="spacer"  id="spacer_0"></div></td></tr>
	    <tr><td><div class="content second" id="content_1"></div></td></tr>
	    <tr><td><div class="spacer"  id="spacer_1"></div></td></tr>
	    <tr><td><div class="content third" id="content_2"></div></td></tr>
	    <tr><td><div class="spacer"  id="spacer_2"></div></td></tr>
	</table>

	<div><br/>
	    x&nbsp; <span id="xLog"></span><br/>
	    y&nbsp; <span id="yLog"></span><br/>
	    dx&nbsp; <span id="dxLog"></span><br/>
	    dy&nbsp; <span id="dyLog"></span><br/>
	    datumX&nbsp; <span id="datumXLog"></span><br/>
	    datumY&nbsp; <span id="datumYLog"></span><br/>
	    mouse &nbsp; <span id="mouseLog"></span><br/>
	    body mouse &nbsp; <span id="bodyMouseLog"></span>
	</div>

Something simlper which is working: (moving around html div elements with d3 drag behavior - the elements moving (on which drag is called) have no "side effects" on other elements, and their style.left and style.top are in one-to-one with x and y of the drag behavior, so very simple, what I'm trying to do is more complicated.) Just to show that it is possible to use drag behavior on non-svg elements.

var width = 240,
    height = 125;

var drag = d3.behavior.drag()
    .origin(function(d) { return d; })
    .on("drag", dragmove);

var data = [
    {x: 0, y: 0, color: "yellow"},
    {x: width, y: 0, color: "green"},
    {x: 0, y: height, color: "orange"},
    {x: width, y: height, color: "magenta"}
];

var div;

    div = d3.select("body").selectAll("div").data(data).enter().append("div")
        .style({
            width: width + "px",
            height: height + "px",
            "background-color": function(d, i){ return d.color},
            position: "absolute",
            left: function(d, i){ return d.x + "px"; },
            top: function(d, i) { return d.y + "px"; }
        })

    div.call(drag);
    
    function dragmove(d) {
    d.x = d3.event.x;
    d.y = d3.event.y;
    leftVal = d.x + "px";
    topVal = d.y + "px";
    d3.select(this)
        .style({left: leftVal, top: topVal})
        .html("x: " + d.x + "<br /> y:" + d.y + "<br /> dx:" + d3.event.dx + "<br /> dy: " + d3.event.dy);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
I can't get anything to drag in your first fiddle (on Chrome). - Lars Kotthoff
works for me - Chrome version 35. - mathheadinclouds
what you dray is the BORDERS between the table cells. Cursor even changes to indicate that. - mathheadinclouds
well thanks for the div drag demonstration anyway! - reabow