title: Getting X-Axis to re-scale during a d3js zoom
body: <p>I'm building a [bar] graph using D3, and I have everything working except one wee bit:</p>
  
  <p>When I zoom in, the graph changes, but the X-Axis doesn't.</p>
  
  <p>As I understand it, I need the zoom on my 'svg' element, because I want everything attached to that to zoom - if I attach the zoom function to the nested 'g' element, I only zoom when I'm over a graph element attached to the 'g' element... and not the white space between.</p>
  
  <p>(and, secondarily, how to I get the zooming function to operate at the cursor location, rather than the "just nearby" that it currently seems to do)</p>
  
  <p>The current page is at <a href="http://lucas.ucs.ed.ac.uk/test/stacked_area_ian.html" rel="nofollow">http://lucas.ucs.ed.ac.uk/test/stacked_area_ian.html</a></p>
  
  <p>(I'd have put it up on jfiddle, but I couldn't figure out how to make the URL data-load work, and didn't fancy adding an 8,000-line data-structure to the javascript :) )</p>
  
  <p>and the javascript code is</p>
  
  <pre><code>/* The size of the chart
   * The whole thing sits in a 'frame' - $body_width wide, 500px high
   * "margin" is the space between the SVG 'frame' and the actual graph
   * "width" and "height" are derived from the actual space of the SVG 'frame'
   * Note there are two graphs, therefore two margins &amp; two heights
   */
  var margin = {top: 10, right: 80, bottom: 350, left: 80},
      body_width = parseInt(d3.select('body').style('width').replace('px','')),
      width = body_width - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;
  
  // Routine for parsing a string into a date object
  var parseDate = d3.time.format("%M %H %d %m %Y").parse;
  
  
  /* "x" and "y" scales.
   * "x" is time, "y" is a count.
   * .range() is the length of the scale
   * .domain() [done later] is the actual range of the values
   * Note that "width" is [0,nnn] whilst "height" is [nnn,0] as the
   * origin in a web page is top left, but the graph is bottom-left
   * see http://www.jeromecukier.net/blog/2011/08/11/d3-scales-and-color/
   */
  var x = d3.time.scale().range([0, width])
                         .rangeRound([0, width], .1),
      y = d3.scale.linear().range([height, 0]);
  
  /* The "xAxis" and "yAxis" are the actual axis lines
   * The scales come from the "scales" above
   * We automatically have tick-marks
   * Axis labels are provided 
   */
  var xAxis  = d3.svg.axis().scale(x).orient("bottom"),
      yAxis  = d3.svg.axis().scale(y).orient("left");
  
  var make_x_axis = function () {
    console.log("make_x_axis called");
      return d3.svg.axis()
          .scale(x)
          .orient("bottom");
  };
  
  /* The zoom trigger - which is centered on the cursor, and zooms just the
   * x scale
   */
  var zoom = d3.behavior.zoom()
      .x(x)
      .on("zoom", zoomed);
  
  /* Now we set up some areas on the canvas
   * "svg" is our Scalar Vector Graphics element in the xhtml DOM
   *   This is placed inside an element identified by an 'id' attribute,
   *   and this element needs to appear in the xhtml *before* this j'script
   */
  var svg = d3.select("#for_svg").append("svg")
      .attr("class", "chart")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .call(zoom)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
  var clip = svg.append("svg:clipPath")
      .attr("id", "clip")
      .append("svg:rect")
      .attr("x", 0)
      .attr("y", 0)
      .attr("width", width)
      .attr("height", height);
  
  var chartBody = svg.append("g")
      .attr("clip-path", "url(#clip)");
  
  /* Having got everything set up, we can pull in our data
   * d3.csv automagically knows how to read .csv files &amp; convert them
   * into JSON data for the function to use.
   * It also knows to take the first line as the 
   *
   * The 'clean' function modifies the data as its read in
   */
  d3.csv("http://lucas.ucs.ed.ac.uk/test/area_ingest.csv", clean, function(data) {
  
    /* We can now set the xAxis domain, based on what's come in.
     * d3.extent returns the lower &amp; upper values of everything in the array
     * passed in
     * d3.max just returns the largest number
     * 
     * Also note that, whilst the *SCALE* goes [max,0] the domain is going 0
     * to max.
     * Again, refer to the Jerome Cukier page
     */
    x.domain(d3.extent(data.map(function(d) { return d.date; })));
    y.domain([0, d3.max(data.map(function(d) { return d.count; }))]);
  
    /* Add the X &amp; Y axis
     * This has to happen in here, otherwise we don't know the values for
     * the xAxis properly
     */
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
     .append("text")
       .attr("transform", "translate(" + (width / 2) + ")")
       .attr("dy", "3em")
       .text("Time");
  
    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
     .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 0 - 40)
        .attr("x",0 - (height / 2))
        .attr("dy", "1em")
        .style("text-anchor", "middle")
        .text("Total Deposits at time");
  
    /* And finally, the bars */
    chartBody.selectAll(".bar")
        .data(data)
      .enter().append("rect")
        .attr("class", "bar")
        .attr("x", function (d) { return (x(d.date)); })
        .attr("width", (width/data.length)*0.8)
        .attr("y", function(d) { return y(d.count); })
        .attr("height", function(d) { return height - y(d.count); });
  
  
  });
  
  function zoomed() {
    svg.selectAll(".bar")
    .attr("transform", "translate(" + d3.event.translate[0] + ",0)scale(" + d3.event.scale     + ", 1)");
    svg.call(make_x_axis);
  }
  
  /* A function to clean the data
   * - date gets parsed into a date format
   * - count gets thrown into a numberic context
   */
  function clean(d) {
    d.date = parseDate(d.date);
    d.count = +d.count;
    return d;
  };
  </code></pre>