0

I have an svg graph using d3.js, but I'm having trouble with the viewbox. As I have it now, the distance under the graph (30px) is fine, but there is still too much space between the text above the graph (48 px).

I don't have much experience with viewbox yet, so I researched and the best answer I found was at Find svg viewbox that trim whitespace around where the solution was to use the bounding box. That answer involves creating a button to click to show the graph correctly presented, but I want it to be positioned without additional user input.

Here is the html code above the graph:

<h1 class="h1_info">First Entry</h1><br>
<div class="zebra_01">This is the general text to describe the issue.<br><br>
With no further improvements:<ul><li>One item was this tall<br>
</li><li>Another item was that tall</li><li>
<span class="span_00">The first is taller than the  second</span></li></ul>
</div>

<svg viewBox="0 0 700 100" preserveAspectRatio="xMinYMid meet" class="d3svg"></svg>
<script src="JS\D3_BarChart.js"></script>

Other html code starts here, 30px below the graph.

Here is the javascript:

var dataArray = [23, 13];
var colors = [ "red", "green" ];

var names = [ "First Name", "Second Name" ];
var widths = [ "5", "700" ]
var dists = ["45", "40"]

var svg = d3.select("svg.d3svg")
  .attr("height", "auto")
  .attr("width", "100%")

var bar = svg.selectAll("g")
  .data(dataArray)
  .enter().append("g")

var gradient = svg
    .append("linearGradient")
    .attr("y1", "0%")
    .attr("y2", "20%")
    .attr("x1", "0%")
    .attr("x2", "25%")
    .attr("id", "gradient")
    .attr("gradientUnits", "userSpaceOnUse")

gradient
    .append("stop")
    .attr('class', 'start')
    .attr("offset", "0%")
    .attr("stop-color", "red")
    .attr("stop-opacity", 1);

gradient
    .append("stop")
    .attr('class', 'end')
    .attr("offset", "100%")
    .attr("stop-color", "green")
    .attr("stop-opacity", 1);

var rect = bar.append('rect')
  .attr("height", "7")
  .attr("width", function(d, i) { return widths[i] })
  .attr("y", function(d, i) { return (i * dists[i]) + 30 })
  .attr("x", "0")
  .attr("fill", "url(#gradient)")

var text = bar.append('text')
  .attr("class", "text-svg")
  .text (function(d, i) { return names[i] })
  .attr("x", "0")
  .attr("y", function(d, i) { return (i * dists[i]) + 55 });

So my question is whether using bounding box is the correct solution, and how can it be set without using a button?

Thanks for any help on this.

RTC222
  • 2,025
  • 1
  • 20
  • 53
  • 1
    If you know you will be starting the first element from `y=30` then you can adjust your viewBox like this: `viewBox="0 30 700 100"`. This can also be done programmatically via d3 after you draw the rectangles. – jrook Oct 02 '18 at 19:54
  • Fantastic. That worked well. Thanks very much. – RTC222 Oct 02 '18 at 19:59

1 Answers1

2

Looking at the chart, the first element starts at y =30. You can change the viewBox to be consistent with this fact:

viewBox="0 30 700 100"

Alternatively, if you are not sure about the y coordinates of the first element, you can use d3 to programmatically adjust the viewBox. Here is the relevant piece of your code:

var rect = bar.append('rect')
        .attr("height", "7")
        .attr("width", function(d, i) { return widths[i] })
        .attr("y", function(d, i) { return (i * dists[i]) + 30 })
        .attr("x", "0")
        .attr("fill", "url(#gradient)")
    svg.attr("viewBox", `0 ${rect.attr("y")} 700 100`) //add this. It uses es6 way to interpolate a string

The last line takes the y attribute of the rect (the topmost element in this case) and changes the viewBox accordingly.

jrook
  • 3,459
  • 1
  • 16
  • 33