0

I have a Gantt chart that I built with D3 that has an associated filter where a user can select the shortest time, longest time, etc. So when the filter changes the xAxis modifies and some of the elements shift with time.

A have also drawn some lines connecting the elements selected by the filter.

When a selection is made in the filter, I remove the lines (via remove), change the color of the rectangles, and change the axis to the new timescale. However when i go to draw new lines, the references are being pulled from the old rectangles, prior to updating.

Here is the JSFiddle showing the issue: https://jsfiddle.net/3afumu4d/12/

To see issue: click the filter icon, then select "Shortest Time", the arrows will redraw but in the wrong location. It is drawing them in the location of rectangles prior to transition.

Here is my update method.

function updateData(){

//remove the lines  
    d3.select("#barsGroup").selectAll('line').remove();
    //remove the arrows
    d3.select("#barsGroup").selectAll('polyline').remove();

//based on selection, calculate start and end times
    var checked = $('input[type=radio][name=timeline]:checked').attr('id');
    taskArray.forEach(function(entry){

        //if it is selected calculate its new start time.
        if(illuminate(entry,checked)){
            var newVal = root.startYear + getDelay(entry, checked);
            entry.startTime = newVal.toString();
            entry.endTime = (newVal + entry.time).toString();
        }


    });


    var minX = d3.min(taskArray, function(d) {return dateFormat.parse(d.startTime);});
    var maxX = d3.max(taskArray, function(d) {return dateFormat.parse(d.endTime);});

    timeScale = d3.time.scale().domain([minX,maxX]).range([0,w-150]);

    //update the boxes
    var chart = d3.select("#innercontainer").transition();

    //get all the rectangles, update all the rectangles
    var updateBoxes = d3.select("#barsGroup").selectAll('rect')
        .data(taskArray)
        .transition()
        .attr("x", function(d){return timeScale(dateFormat.parse(d.startTime)) + sidePadding;})
        .attr("y", function(d, i){return i*gap + topPadding;})
        .attr("width", function(d){return (timeScale(dateFormat.parse(d.endTime))-timeScale(dateFormat.parse(d.startTime)))})
        .attr("fill", function(d){
          for (var i = 0; i < categories.length; i++){
              if (d.component == categories[i]){
                //if true matches selection in filter
                var checked = $('input[type=radio][name=timeline]:checked').attr('id');
                if(illuminate(d,checked)){
                    return d3.rgb(colorScale(i));
                }else{
                    return d3.rgb(colorScale(i)).darker(5);
                }


                //else show darker
              }
          }

         })
        .attr("opacity", function(d){
            for (var i = 0; i < categories.length; i++){
              if (d.component == categories[i]){
                //if true matches selection in filter
                var checked = $('input[type=radio][name=timeline]:checked').attr('id');
                if(illuminate(d,checked)){
                    return 1.0;
                }else{
                    return 0.4;
                }


                //else show darker
              }
          }
        });

    //Update the text boxes
    var textlist = d3.select("#barsGroup").selectAll('text')
    .data(taskArray)
    .transition()
    .attr("x", function(d){
         return (timeScale(dateFormat.parse(d.endTime))-timeScale(dateFormat.parse(d.startTime)))/2 + timeScale(dateFormat.parse(d.startTime)) + sidePadding;
     })
    .attr("y", function(d, i){
         return i*gap + 8+ topPadding;
     })
    .attr("fill", function(d){
                for (var i = 0; i < categories.length; i++){
                    if (d.component == categories[i]){
                        //if true matches selection in filter
                        var checked = $('input[type=radio][name=timeline]:checked').attr('id');
                        if(illuminate(d,checked)){
                            return "#fff";
                        }else{
                            return "#3f3f3f";
                        }
                    }
            }
        }
    );



    //update grid lines

    //scale the axis
    xAxis = d3.svg.axis()
        .scale(timeScale)
        .orient('bottom')
        .ticks(d3.time.year, 5) //TODO: change to years (skip every ten?)
        .tickSize(-h+topPadding+20, 0, 0)
        .tickFormat(d3.time.format('%Y'))

    d3.select(".grid")
    .transition()
    .duration(750)
    .call(xAxis)
    .selectAll("text")  
        .style("text-anchor", "middle")
        .attr("fill", "#fff")
        .attr("stroke", "none")
        .attr("font-size", 10)
        .attr("dy", "1em")
        .each("end",drawArrows(taskArray,checked));;

}

The drawArrows method is what adds arrows to the newly highlighted path through the Gantt Chart. But its not getting new values.

Is there any way I can fire the drawArrows method after the DOM elements have updated?

Setix
  • 141
  • 2
  • 18
  • I'm not sure what your question is -- could you put something on jsfiddle or similar that demonstrates the problem please? – Lars Kotthoff Jun 28 '15 at 00:31
  • I made a jsfiddle of my chart...sorry about the complexity but it's what I'm working with. The actual javascript starts at like 1167. To see the problem, click the filter, and select Shortest Time. The arrows that get drawn are in the position of the rectangles prior to the transitions. Sorry I'm new to D3. https://jsfiddle.net/3afumu4d/12/ – Setix Jun 28 '15 at 17:00
  • Trying to introduce features directly into an 1,000 line+ application without testing it in isolation first is almost as crazy as expecting someone to come in cold and fix it for you. Don't you think it would be smarter to isolate the feature you are trying to implement and get it working before you integrate it? – Cool Blue Jun 28 '15 at 17:41
  • The arrows are directly dependent in the on the positions of the rectangles as they transition. All I'm trying to do is draw lines based on the rectangles position post transition. and its not 1,000+ lines...most of it is JSON data. Just out of curiosity, when you build an app for every new feature do YOU create a separate app to test? Probably not, unless your deadlines are infinite. – Setix Jun 28 '15 at 18:30

1 Answers1

0

I found the answer in another Stack Overflow question.

Essentially I needed to wait for all "rect" transitions to complete, so the rect x,y, width values were updated.

I used the endAll method approach found here: d3.js transition end event

Community
  • 1
  • 1
Setix
  • 141
  • 2
  • 18