score:1

Accepted answer

It seems to me that you want to adapt the ticks count to the width, not the other way around (more on that below). That being the case, instead of using an interval, like d3.timeDay, just set the (approximate) number of ticks:

axis.ticks(10)

However, if in fact you want to modify the width of the SVG, you can count the number of days and set the SVG width accordingly:

var dayWidth = 30;//set the width for each day here
var numberOfDays = d3.timeDay.count(timeScale.domain()[0], timeScale.domain()[1]);//get the total number of days in the data
w = numberOfDays * dayWidth;
svg.attr("width", w);

This is just a quick code for you to see how you can do it, a better solution sets the margins accordingly (I didn't do it because you have lots of magical numbers in your code).

Regarding the height, you can calculate it based on the number of tasks, and set the SVG accordingly:

h = tasks.length * gap + topPadding + 40;
svg.attr("height", h);

Bear in mind that you still have magic numbers here. Try to avoid them.

Here is the code with those change:

$(document).ready(function() {

  myObj = JSON.parse('{"QUAL":[{"task": "milk", "type": "Ordered", "startTime": "14/10", "endTime": "16/11"},{"task": "butter", "type": "Completed", "startTime": "22/09", "endTime": "23/09"},{"task": "butter", "type": "Completed", "startTime": "24/09", "endTime": "25/09"},{"task": "bread", "type": "Completed", "startTime": "04/10", "endTime": "15/10"},{"task": "water", "type": "Completed", "startTime": "11/10", "endTime": "16/10"},{"task": "fish", "type": "Discontinued", "startTime": "21/09", "endTime": "23/09"},{"task": "mince", "type": "Discontinued", "startTime": "26/09", "endTime": "27/09"},{"task": "soda", "type": "Discontinued", "startTime": "04/10", "endTime": "08/10"},{"task": "sugar", "type": "Discontinued", "startTime": "04/10", "endTime": "08/10"},{"task": "flour", "type": "Discontinued", "startTime": "09/10", "endTime": "11/10"},{"task": "shampoo", "type": "Discontinued", "startTime": "10/10", "endTime": "11/10"},{"task": "salt", "type": "On Hold", "startTime": "04/10", "endTime": "04/10"}]}')



  myObj2 = JSON.parse('{"QUAL":[{"task": "milk", "type": "Ordered", "startTime": "14/10", "endTime": "16/11"},{"task": "butter", "type": "Completed", "startTime": "22/09", "endTime": "23/09"},{"task": "butter", "type": "Completed", "startTime": "24/09", "endTime": "25/09"},{"task": "bread", "type": "Completed", "startTime": "04/10", "endTime": "15/10"},{"task": "water", "type": "Completed", "startTime": "11/10", "endTime": "16/10"},{"task": "fish", "type": "Discontinued", "startTime": "21/09", "endTime": "23/09"},{"task": "mince", "type": "Discontinued", "startTime": "26/09", "endTime": "27/09"}]}')


  var taskArray = [];


  $('.modal').on('hidden.bs.modal', function() {

    document.getElementById("chart").innerHTML = ""
    taskArray = [];

  });


  $('.modal').on('show.bs.modal', function() {

  });



  $('#btn1').on('click', function() {

    $.each(myObj.QUAL, function(i, j) {
      taskArray.push({
        task: j.task,
        type: j.type,
        startTime: j.startTime,
        endTime: j.endTime
      })
    })
    makeChart();
  });


  $('#btn2').on('click', function() {



    $.each(myObj2.QUAL, function(i, j) {
      taskArray.push({
        task: j.task,
        type: j.type,
        startTime: j.startTime,
        endTime: j.endTime
      })
    })
    makeChart();
  });

  makeChart = function() {


    var w,
      h,
      dayWidth = 30;

    $('#amsModal').modal({
      backdrop: 'static'
    });
    // $('#amsDetailBodyImage').css('display', 'none');

    var svg = d3.selectAll("#chart")
      //.selectAll("svg")
      .append("svg")
      .attr("class", "svg");

    //var dateFormat = d3.timeParse("%Y-%m-%d");
    var dateFormat = d3.timeParse("%d/%m");

    var timeScale = d3.scaleTime()
      .domain([d3.min(taskArray, function(d) {
          return dateFormat(d.startTime);
        }),
        d3.max(taskArray, function(d) {
          return dateFormat(d.endTime);
        })
      ]);

    var numberOfDays = d3.timeDay.count(timeScale.domain()[0], timeScale.domain()[1]);

    w = numberOfDays * dayWidth;

    svg.attr("width", w);

    timeScale.range([w - 150, 0]);

    var categories = new Array();

    for (var i = 0; i < taskArray.length; i++) {
      categories.push(taskArray[i].type);
    }

    var catsUnfiltered = categories; //for vert labels

    categories = checkUnique(categories);

    makeGant(taskArray, w);

    var title = svg.append("text")
      .text("Groceries")
      .attr("x", w / 2)
      .attr("y", 25)
      .attr("text-anchor", "middle")
      .attr("font-size", 18)
      .attr("fill", "#000000");



    function makeGant(tasks, pageWidth) {

      var barHeight = 20;
      var gap = barHeight + 4;
      var topPadding = 75;
      var sidePadding = 75;

      h = tasks.length * gap + topPadding + 40;
      svg.attr("height", h);

      var colorScale = d3.scaleLinear()
        .domain([0, categories.length])
        .range(["#00B9FA", "#F95002"])
        .interpolate(d3.interpolateHcl);

      makeGrid(sidePadding, topPadding, pageWidth, h);
      drawRects(tasks, gap, topPadding, sidePadding, barHeight, colorScale, pageWidth, h);
      vertLabels(gap, topPadding, sidePadding, barHeight, colorScale);


      //$('.modal-button').attr('disabled', true);
    }


    function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {

      var bigRects = svg.append("g")
        .selectAll("rect")
        .data(theArray)
        .enter()
        .append("rect")
        .attr("x", 0)
        .attr("y", function(d, i) {
          return i * theGap + theTopPad - 2;
        })
        .attr("width", function(d) {
          return w - theSidePad / 2;
        })
        .attr("height", theGap)
        .attr("stroke", "none")
        .attr("fill", function(d) {
          for (var i = 0; i < categories.length; i++) {
            if (d.type == categories[i]) {
              return d3.rgb(theColorScale(i));
            }
          }
        })
        .attr("opacity", 0.2);


      var rectangles = svg.append('g')
        .selectAll("rect")
        .data(theArray)
        .enter();


      var innerRects = rectangles.append("rect")
        .attr("rx", 3)
        .attr("ry", 3)
        .attr("x", function(d) {
          // return timeScale(dateFormat(d.startTime)) + theSidePad;
          return timeScale(dateFormat(d.endTime)) + theSidePad;
        })
        .attr("y", function(d, i) {
          return i * theGap + theTopPad;
        })
        .attr("width", function(d) {
          //return (timeScale(dateFormat(d.endTime))-timeScale(dateFormat(d.startTime)));
          return (timeScale(dateFormat(d.startTime)) - timeScale(dateFormat(d.endTime)));
        })
        .attr("height", theBarHeight)
        .attr("stroke", "none")
        .attr("fill", function(d) {
          for (var i = 0; i < categories.length; i++) {
            if (d.type == categories[i]) {
              return d3.rgb(theColorScale(i));
            }
          }
        })


      var rectText = rectangles.append("text")
        .text(function(d) {
          return d.task;
        })
        .attr("x", function(d) {
          return (timeScale(dateFormat(d.endTime)) - timeScale(dateFormat(d.startTime))) / 2 + timeScale(dateFormat(d.startTime)) + theSidePad;
        })
        .attr("y", function(d, i) {
          return i * theGap + 14 + theTopPad;
        })
        .attr("font-size", 11)
        .attr("text-anchor", "middle")
        .attr("text-height", theBarHeight)
        .attr("fill", "#000000");


      rectText.on('mouseover', function(e) {
        // console.log(this.x.animVal.getItem(this));
        var tag = "";

        if (d3.select(this).data()[0].details != undefined) {
          tag = "Task: " + d3.select(this).data()[0].task + "<br/>" +
            "Type: " + d3.select(this).data()[0].type + "<br/>" +
            "Starts: " + d3.select(this).data()[0].startTime + "<br/>" +
            "Ends: " + d3.select(this).data()[0].endTime + "<br/>" +
            "Details: " + d3.select(this).data()[0].details;
        } else {
          tag = "Task: " + d3.select(this).data()[0].task + "<br/>" +
            "Type: " + d3.select(this).data()[0].type + "<br/>" +
            "Starts: " + d3.select(this).data()[0].startTime + "<br/>" +
            "Ends: " + d3.select(this).data()[0].endTime;
        }
        var output = document.getElementById("tag");

        var x = this.x.animVal.getItem(this) + "px";
        var y = this.y.animVal.getItem(this) + 25 + "px";

        output.innerHTML = tag;
        output.style.top = y;
        output.style.left = x;
        output.style.display = "block";
      }).on('mouseout', function() {
        var output = document.getElementById("tag");
        output.style.display = "none";
      });


      innerRects.on('mouseover', function(e) {
        //console.log(this);
        var tag = "";

        if (d3.select(this).data()[0].details != undefined) {
          tag = "Task: " + d3.select(this).data()[0].task + "<br/>" +
            "Type: " + d3.select(this).data()[0].type + "<br/>" +
            "Starts: " + d3.select(this).data()[0].startTime + "<br/>" +
            "Ends: " + d3.select(this).data()[0].endTime + "<br/>" +
            "Details: " + d3.select(this).data()[0].details;
        } else {
          tag = "Task: " + d3.select(this).data()[0].task + "<br/>" +
            "Type: " + d3.select(this).data()[0].type + "<br/>" +
            "Starts: " + d3.select(this).data()[0].startTime + "<br/>" +
            "Ends: " + d3.select(this).data()[0].endTime;
        }
        var output = document.getElementById("tag");

        var x = (this.x.animVal.value + this.width.animVal.value / 2) + "px";
        var y = this.y.animVal.value + 25 + "px";

        output.innerHTML = tag;
        output.style.top = y;
        output.style.left = x;
        output.style.display = "block";
      }).on('mouseout', function() {
        var output = document.getElementById("tag");
        output.style.display = "none";

      });
    }


    function makeGrid(theSidePad, theTopPad, w, h) {

      var xAxis = d3.axisBottom(timeScale)
        .ticks(d3.timeDay, 1)
        .tickSize(-h + theTopPad + 20, 0, 0)
        //			.tickFormat(d3.timeFormat('%d %b'));
        .tickFormat(d3.timeFormat('%d/%m'));
      var grid = svg.append('g')
        .attr('class', 'grid')
        //			.attr('transform', 'translate(' +theSidePad + ', ' + (h - 50) + ')')
        .attr('transform', 'translate(' + theSidePad + ', ' + (h - 20) + ')')
        .call(xAxis)
        .selectAll("text")
        .style("text-anchor", "middle")
        .attr("fill", "#000000")
        .attr("stroke", "none")
        .attr("font-size", 10)
        //							.attr("dy", "1em")
        .attr("dy", "0.35em")
        .attr("transform", "rotate(-65)");

    }

    function vertLabels(theGap, theTopPad, theSidePad, theBarHeight, theColorScale) {
      var numOccurances = new Array();
      var prevGap = 0;

      for (var i = 0; i < categories.length; i++) {
        numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)];
      }

      var axisText = svg.append("g") //without doing this, impossible to put grid lines behind text
        .selectAll("text")
        .data(numOccurances)
        .enter()
        .append("text")
        .text(function(d) {
          return d[0];
        })
        .attr("x", 10)
        .attr("y", function(d, i) {
          if (i > 0) {
            for (var j = 0; j < i; j++) {
              prevGap += numOccurances[i - 1][1];
              // console.log(prevGap);
              return d[1] * theGap / 2 + prevGap * theGap + theTopPad;
            }
          } else {
            return d[1] * theGap / 2 + theTopPad;
          }
        })
        .attr("font-size", 11)
        .attr("text-anchor", "start")
        .attr("text-height", 14)
        .attr("fill", function(d) {
          for (var i = 0; i < categories.length; i++) {
            if (d[0] == categories[i]) {
              //  console.log("true!");
              return d3.rgb(theColorScale(i)).darker();
            }
          }
        });

    }

    //from this stackexchange question: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
    function checkUnique(arr) {
      var hash = {},
        result = [];
      for (var i = 0, l = arr.length; i < l; ++i) {
        if (!hash.hasOwnProperty(arr[i])) { //it works with objects! in FF, at least
          hash[arr[i]] = true;
          result.push(arr[i]);
        }
      }
      return result;
    }

    //from this stackexchange question: http://stackoverflow.com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array
    function getCounts(arr) {
      var i = arr.length, // var to loop over
        obj = {}; // obj to store results
      while (i) obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
      return obj;
    }

    // get specific from everything
    function getCount(word, arr) {
      return getCounts(arr)[word] || 0;
    }

    $('#amsModal').modal({
      backdrop: 'static'
    });

  };



});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<!DOCTYPE html>
<html>

<style>
  * {
    margin: 0;
    padding: 0;
  }
  
  #container {
    margin: 0 auto;
    position: relative;
    /*width: 1000px;*/
    overflow: visible;
  }
  
  #chart {
    /*    width: 800px;
    height: 400px;*/
    overflow: scroll;
    /*position: absolute;*/
  }
  
  .grid .tick {
    stroke: lightgrey;
    opacity: 0.3;
    shape-rendering: crispEdges;
  }
  
  .grid path {
    stroke-width: 0;
  }
  
  #tag {
    color: white;
    background: #FA283D;
    width: 150px;
    position: absolute;
    display: none;
    padding: 3px 6px;
    margin-left: -80px;
    font-size: 11px;
  }
  
  #tag:before {
    border: solid transparent;
    content: ' ';
    height: 0;
    left: 50%;
    margin-left: -5px;
    position: absolute;
    width: 0;
    border-width: 10px;
    border-bottom-color: #FA283D;
    top: -20px;
  }
  
  .container-fluid {
    margin-left: 10%;
    margin-right: 10%;
  }
  
  .button {
    max-width: 200px;
  }
  
  .modal-body-detail {
    max-height: calc(100vh - 200px);
    overflow-y: auto;
  }
</style>

<head>
  <title>Meh</title>
</head>

<body>

  <div class='container-fluid'>
    <div class="row">

      <button id="btn1" type="button" class="btn btn-primary">
          Long
        </button>

      <button id="btn2" type="button" class="btn btn-primary">
          Short
        </button>
    </div>


    <div class="modal fade autoModal " id="amsModal" tabindex="-1" role="dialog" aria-labelledby="amsModalLabel" aria-hidden="true">
      <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <h4 class="modal-title" id="amsModalLabel">Orders</h4>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                <span aria-hidden="true">&times;</span>
              </button>
          </div>
          <div id="detailBody" class="modal-body ">
            <div id="container">
              <div id="chart"></div>
              <!-- chart -->
              <div id="tag"></div>
              <!-- tooltip on hover -->
            </div>
          </div>


          <div class="modal-footer">
            <button type="button" class="btn btn-secondary modal-button" data-dismiss="modal">Close</button>
          </div>
        </div>
      </div>
    </div>



  </div>

</body>

</html>


Related Query