Home > Blockchain >  Fix x-axis position in scrollable Heatmap d3
Fix x-axis position in scrollable Heatmap d3

Time:12-02

I created a scrollable heatmap and I want to make the x-axis fixed when scrolling the y-axis. I checked some posts here and tried to make the solutions work for example with position fixed, but it did not work out.

Edit: I tried to solve it with this example Fix x-axis position in scrollable Heatmap d3 but now my x-axis description disappeared and the x-axis is placed at the bottom.

var width = 500,
  height = 600,
  margintop = 50,
  marginbottom = 50,
  marginright = 10,
  marginleft = 50
d3.csv("https://raw.githubusercontent.com/Lea1216/d3/main/heatmap.csv", function(data) {

  var svg = d3.select("#my_dataviz")
              .append("div")
              .classed("chart",true)
              .append("svg")
                .attr("width",width  marginleft   marginright)
                .attr("height",height margintop   marginbottom)
                .append("g")
                .attr("transform",
                      "translate("   marginleft   ","   margintop   ")");

 var axis = d3.select("#my_dataviz")
                   .append("svg")
                   .attr("width", width   marginleft  marginright)
                   .attr("height",40)
                   .append("g")
                   .attr("transform", "translate("   marginleft   ", 0)");


  var x_axis = d3.scaleBand()
    .range([0, width])
    .domain(data.map(function(d) {
      return d.group;
    }))
    .padding(0.01);

  axis.call(d3.axisTop(x_axis))
                .selectAll("text")
                .style("text-anchor", "end")
                .style("position","fixed")
                .attr("dx",15)
                .attr("dy",5)
                .attr("transform", "rotate(-65)");

  var y_axis = d3.scaleBand()
    .range([height, 0])
    .domain(data.map(function(d) {
      return d.activity;
    }))
    .padding(0.01);

  svg.append("g")
    .call(d3.axisLeft(y_axis))
    .attr("class", "y_axis")
    .selectAll("text")
    .on("click", function(d) {
      window.open(d.url, "_blank")
    });

  var myColor = d3.scaleLinear()
    .range(["white", "#C37B89"])
    .domain([1, 100])

  svg.selectAll()
    .data(data, function(d) {
      return d.group   ':'   d.activity;
    })
    .enter()
    .append("rect")
    .attr("x", function(d) {
      return x_axis(d.group)
    })
    .attr("y", function(d) {
      return y_axis(d.activity)
    })
    .attr("width", x_axis.bandwidth())
    .attr("height", y_axis.bandwidth())
    .style("fill", function(d) {
      return myColor(d.value)
    })
    .style("stroke-width", 1)
    .style("stroke", "none")

})
.rect {
  opacity: 0.8;
}

#my_dataviz {
  width: 600px;
  height: 500px;
  overflow-y: scroll;
  padding: 50px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="my_dataviz"></div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

There are several possible solutions for this. The first one is to draw two SVG elements on the same level and use position: fixed on the X-axis element. You can see that the blocks still show up behind the axis, but you could fix that by drawing a rect the total size of the axis SVG element and giving it fill: white:

var width = 500,
  height = 600,
  margintop = 50,
  marginbottom = 50,
  marginright = 10,
  marginleft = 50
d3.csv("https://raw.githubusercontent.com/Lea1216/d3/main/heatmap.csv", function(data) {

  // Add the axis *before* adding the SVG, because the order matters in HTML
  var axis = d3.select("#my_dataviz")
    .append("svg")
    .attr("width", width   marginleft   marginright)
    // Add 2 so you have a little bit of room left for the black bar
    // i.e. margin top has to be less than total height!
    .attr("height", margintop   2)
    .style("position", "fixed") // this makes the axis fixed
    .append("g")
    .attr("transform", "translate("   marginleft   ", "   margintop   ")");

  var svg = d3.select("#my_dataviz")
    .append("svg")
    .attr("width", width   marginleft   marginright)
    .attr("height", height   margintop   marginbottom)
    .append("g")
    .attr("transform", "translate("   marginleft   ", "   margintop   ")");

  var x_axis = d3.scaleBand()
    .range([0, width])
    .domain(data.map(function(d) {
      return d.group;
    }))
    .padding(0.01);

  axis.call(d3.axisTop(x_axis))
    .selectAll("text")
    .style("text-anchor", "end")
    .style("position", "fixed")
    .attr("dx", 15)
    .attr("dy", 5)
    .attr("transform", "rotate(-65)");

  var y_axis = d3.scaleBand()
    .range([height, 0])
    .domain(data.map(function(d) {
      return d.activity;
    }))
    .padding(0.01);

  svg.append("g")
    .call(d3.axisLeft(y_axis))
    .attr("class", "y_axis")
    .selectAll("text")
    .on("click", function(d) {
      window.open(d.url, "_blank")
    });

  var myColor = d3.scaleLinear()
    .range(["white", "#C37B89"])
    .domain([1, 100])

  svg.selectAll()
    .data(data, function(d) {
      return d.group   ':'   d.activity;
    })
    .enter()
    .append("rect")
    .attr("x", function(d) {
      return x_axis(d.group)
    })
    .attr("y", function(d) {
      return y_axis(d.activity)
    })
    .attr("width", x_axis.bandwidth())
    .attr("height", y_axis.bandwidth())
    .style("fill", function(d) {
      return myColor(d.value)
    })
    .style("stroke-width", 1)
    .style("stroke", "none")

})
.rect {
  opacity: 0.8;
}

#my_dataviz {
  border: solid 1px red;
  width: 600px;
  height: 300px;
  overflow-y: scroll;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="my_dataviz"></div>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

The second option is to put the other SVG in a div and scroll the div instead of the SVG. Then, you do need to do more with CSS and you need to move some CSS rules from the #my_dataviz element to the .chart element:

var width = 500,
  height = 600,
  margintop = 50,
  marginbottom = 50,
  marginright = 10,
  marginleft = 50
d3.csv("https://raw.githubusercontent.com/Lea1216/d3/main/heatmap.csv", function(data) {

  // Add the axis *before* adding the SVG, because the order matters in HTML
  var axis = d3.select("#my_dataviz")
    .append("svg")
    .attr("width", width   marginleft   marginright)
    // Add 2 so you have a little bit of room left for the black bar
    // i.e. margin top has to be less than total height!
    .attr("height", margintop   1)
    .append("g")
    .attr("transform", "translate("   marginleft   ", "   margintop   ")");

  var svg = d3.select("#my_dataviz")
    .append("div")
    // Note the CSS rules for .chart
    .attr("class", "chart")
    .append("svg")
    .attr("width", width   marginleft   marginright)
    // No margin-top required here, because the other element already took care of it
    .attr("height", height   marginbottom)
    .append("g")
    // Same, no margin-top
    .attr("transform", "translate("   marginleft   ", 0)");

  var x_axis = d3.scaleBand()
    .range([0, width])
    .domain(data.map(function(d) {
      return d.group;
    }))
    .padding(0.01);

  axis.call(d3.axisTop(x_axis))
    .selectAll("text")
    .style("text-anchor", "end")
    .style("position", "fixed")
    .attr("dx", 15)
    .attr("dy", 5)
    .attr("transform", "rotate(-65)");

  var y_axis = d3.scaleBand()
    .range([height, 0])
    .domain(data.map(function(d) {
      return d.activity;
    }))
    .padding(0.01);

  svg.append("g")
    .call(d3.axisLeft(y_axis))
    .attr("class", "y_axis")
    .selectAll("text")
    .on("click", function(d) {
      window.open(d.url, "_blank")
    });

  var myColor = d3.scaleLinear()
    .range(["white", "#C37B89"])
    .domain([1, 100])

  svg.selectAll()
    .data(data, function(d) {
      return d.group   ':'   d.activity;
    })
    .enter()
    .append("rect")
    .attr("x", function(d) {
      return x_axis(d.group)
    })
    .attr("y", function(d) {
      return y_axis(d.activity)
    })
    .attr("width", x_axis.bandwidth())
    .attr("height", y_axis.bandwidth())
    .style("fill", function(d) {
      return myColor(d.value)
    })
    .style("stroke-width", 1)
    .style("stroke", "none")

})
.rect {
  opacity: 0.8;
}

#my_dataviz {
  width: 600px;
  border: solid 1px red;
}

/* To make sure there is no space between the DIV and the SVG */
#my_dataviz > * {
  display: block;
}

.chart {
  overflow-y: scroll;
  max-height: 300px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="my_dataviz"></div>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related