How come my donut graphs are sitting outside the box while all other types of charts are properly aligned?

I am encountering an issue with my charts where all of them are positioned in the center of a div when drawn by the user, except for the donut charts. They seem to be placed outside of the top-left corner instead. Can anyone provide insights as to why this might be happening? I have created a JS Fiddle to demonstrate this.

JS Fiddle

Essentially, I have three functions at play here. The first one is a generic drawChart() function that determines which chart to draw based on the button clicked. Then there's chartTwo(), a simple two-line illustration of how that particular chart should be centered within the div. Lastly, there's chartOne(), responsible for drawing the problematic donut chart that is not behaving as expected.

Generic chart builder func

function drawChart(int){

  var $chartarea = $('#chartarea'),
      ca_w = $chartarea.innerWidth(),
      ca_h = $chartarea.innerHeight();


  if ($chartarea.find('svg').length > 0) {
    $chartarea.find('svg').remove();
  }

  var margin = {top: 20, right: 20, bottom: 20, left: 20};

  var width = ca_w - margin.left - margin.right,
      height = ca_h - margin.top - margin.bottom;

  var g = d3.select('#chartarea').append('svg')
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
    .append('g')
      .style('position', 'relative')
      .style('left', '0')
      .attr('height', height)
      .attr('width', width)
      .attr('transform', 'translate('+margin.left+', '+margin.top+')');

  switch (int) {
    case 0:
      chartOne(g, width, height);
      break;
    case 1:
      chartTwo(g, width, height);
      break;
    default:
      chartOne(g, width, height);
  }

}

Donut chart func

function chartOne(g, width, height) {

  var data = [
    {name: "USA", value: 40},
    {name: "UK", value: 20},
    {name: "Canada", value: 30},
    {name: "Maxico", value: 10},
  ];

  var text = "";

  var thickness = 40;

  var radius = Math.min(width, height) / 2;
  var color = d3.scaleOrdinal(d3.schemeCategory10);

  var arc = d3.arc()
  .innerRadius(radius - thickness)
  .outerRadius(radius);

  var pie = d3.pie()
  .value(function(d) { return d.value; })
  .sort(null);

  g.selectAll('path')
  .data(pie(data))
  .enter()
  .append("g")
  .on("mouseover", function(d) {
        let g = d3.select(this)
          .style("cursor", "pointer")
          .style("fill", "black")
          .append("g")
          .attr("class", "text-group");

        g.append("text")
          .attr("class", "name-text")
          .text(d.data.name)
          .attr('text-anchor', 'middle')
          .attr('dy', '-1.2em');

        g.append("text")
          .attr("class", "value-text")
          .text(d.data.value)
          .attr('text-anchor', 'middle')
          .attr('dy', '.6em');
      })
    .on("mouseout", function() {
        d3.select(this)
          .style("cursor", "none")
          .style("fill", color(this._current))
          .select(".text-group").remove();
      })
    .append('path')
    .attr('d', arc)
    .attr('fill', (d,i) => color(i))
    .on("mouseover", function() {
        d3.select(this)
          .style("cursor", "pointer")
          .style("fill", "black");
      })
    .on("mouseout", function() {
        d3.select(this)
          .style("cursor", "none")
          .style("fill", color(this._current));
      })
    .each(function(d, i) { this._current = i; });


  g.append('text')
    .attr('text-anchor', 'middle')
    .attr('dy', '.35em')
    .text(text);
}

Answer №1

One way to adjust the translate function for both width and height in the chartOne() function is by adding it as follows:

g.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

After incorporating the margins, the final output can be seen in the demo below:

$(function() {

  // on load
  $('li').eq(0).addClass('active');
  drawChart(0);

  $('li').on('click', function() {
    var index = $(this).index();
    $('li').removeClass('active');
    $(this).addClass('active');

    drawChart(index);
  });
});

function drawChart(int) {

  var $chartarea = $('#chartarea'),
    ca_w = $chartarea.innerWidth(),
    ca_h = $chartarea.innerHeight();

  
  if ($chartarea.find('svg').length > 0) {
    $chartarea.find('svg').remove();
  }

  var margin = {
    top: 20,
    right: 20,
    bottom: 20,
    left: 20
  };

  var width = ca_w - margin.left - margin.right,
    height = ca_h - margin.top - margin.bottom;

  var g = d3.select('#chartarea').append('svg')
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append('g')
    .style('position', 'relative')
    .style('left', '0')
    .attr('height', height)
    .attr('width', width)
    .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

  switch (int) {
    case 0:
      chartOne(g, width, height, margin);// edited
      break;
    case 1:
      chartTwo(g, width, height);
      break;
    default:
      chartOne(g, width, height, margin);// edited
  }

}

function chartTwo(g, width, height) {
  g.append('line')
    .attr('x1', 0)
    .attr('y1', 0)
    .attr('x2', width)
    .attr('y2', height)
    .attr('stroke', 'grey')
    .attr('stroke-width', '10px');
  g.append('line')
    .attr('x1', width)
    .attr('y1', 0)
    .attr('x2', 0)
    .attr('y2', height)
    .attr('stroke', 'grey')
    .attr('stroke-width', '10px');
}


function chartOne(g, width, height, margin) { // edited
  // ADDED THIS
  g.attr("transform", "translate(" + (width / 2 + margin.left) + "," + (height / 2 + margin.top) + ")");

  var data = [{
      name: "USA",
      value: 40
    },
    {
      name: "UK",
      value: 20
    },
    {
      name: "Canada",
      value: 30
    },
    {
      name: "Maxico",
      value: 10
    },
  ];

  var text = "";

  var thickness = 40;

  var radius = Math.min(width, height) / 2;
  var color = d3.scaleOrdinal(d3.schemeCategory10);

  var arc = d3.arc()
    .innerRadius(radius - thickness)
    .outerRadius(radius);

  var pie = d3.pie()
    .value(function(d) {
      return d.value;
    })
    .sort(null);

  g.selectAll('path')
    .data(pie(data))
    .enter()
    .append("g")
    .on("mouseover", function(d) {
      let g = d3.select(this)
        .style("cursor", "pointer")
        .style("fill", "black")
        .append("g")
        .attr("class", "text-group");

      g.append("text")
        .attr("class", "name-text")
        .text(d.data.name)
        .attr('text-anchor', 'middle')
        .attr('dy', '-1.2em');

      g.append("text")
        .attr("class", "value-text")
        .text(d.data.value)
        .attr('text-anchor', 'middle')
        .attr('dy', '.6em');
    })
    .on("mouseout", function() {
      d3.select(this)
        .style("cursor", "none")
        .style("fill", color(this._current))
        .select(".text-group").remove();
    })
    .append('path')
    .attr('d', arc)
    .attr('fill', (d, i) => color(i))
    .on("mouseover", function() {
      d3.select(this)
        .style("cursor", "pointer")
        .style("fill", "black");
    })
    .on("mouseout", function() {
      d3.select(this)
        .style("cursor", "none")
        .style("fill", color(this._current));
    })
    .each(function(d, i) {
      this._current = i;
    });


  g.append('text')
    .attr('text-anchor', 'middle')
    .attr('dy', '.35em')
    .text(text);
}
* {
  margin: 0;
  padding: 0;
}

#chartarea {
  margin: 20px;
  border: solid 1px black;
  height: 300px;
  width: 500px;
}

ul {
  display: flex;
  width: 500px;
  margin: 20px;
  list-style: none;
  text-align: center;
}

li {
  margin: 0 20px;
  padding: 5px;
  border-radius: 10px;
  flex: 1;
  background: grey;
  cursor: pointer;
}

li.active {
  background: #60cafe
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>

<div id="chartarea" class="charts--item"></div>

<ul>
  <li>Chart One</li>
  <li>Chart Two</li>
</ul>

Answer №2

Your bar/pie chart is set with its center at [0,0] while your x-axis consists of lines with endpoints as shown below:

.attr('x1', 0)
.attr('y1', 0)
.attr('x2', width)
.attr('y2', height)

The lines in your visualization start and end at the corner whereas the bar/pie chart is positioned centrally at the corner.

To address this issue, a solution would be to create a new group (g) specifically for the pie chart with a different transformation compared to the rest of the charts. This new g should have a translation of [width/2,height/2], effectively placing the center of the pie chart in the middle of the visualization. A demonstration of this workaround can be seen in this example.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unlocking the potential of Three.js with mouse picking

I am looking to implement object picking in the following code snippet: var Three = new function () { this.scene = new THREE.Scene() this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000) this.camera.po ...

Saving a block of text to a database with PHP without the need to refresh the webpage

On my index.php page, I have a paragraph element and an input button: <p id="result">Hello this is test paragraph</p> <input id="insertbutton" type="button" value="insert" /> My goal is to save the paragraph content into a database ...

Do you think there is a more efficient way to solve this issue?

const [active, setActive] = React.useState(["active", "", "", "", ""]);``your unique text`` const hrefs = React.useMemo( () => ["/", "/about", "/skills", "/projects", "/contact"], [] ); React.useEffect(() => { setInterval(() => { ...

When you reach a scrolling distance of over 300 vertical heights,

Is it possible to show and hide a class based on viewport height? I am familiar with displaying and hiding a class after a specified pixel height, but I'm wondering if it's achievable using viewport height instead? Specifically 3 times the viewp ...

Glimmering border in jQuery width transition

I am facing a challenge in developing a horizontal accordion style slider that is not dependent on fixed widths. The issues I encountered include: The 'flickering edge' on the right-hand list item during animation Depending on the max-width set ...

Reply after performing several asynchronous queries using mongoose and express

When trying to create a list of products to send to a client, I am encountering an issue where res.send executes before the loop has completed. I have tried using async/await but it doesn't seem to work. Any suggestions on how to resolve this? Below ...

Implementing translation text into a PHP database

<!doctype html> <html> <head> <meta charset="utf-8"> <title>Translate and Save Text</title> </head> <body> <form action="" method="post" name="theform"> <table width="693" border="1" style="table-l ...

How can the callback from C++ be successfully installed in the Javaobject window within QtWebkit?

I have successfully implemented HTML-JS to call a C++ method using QtWebkit. Now, I want to send a callback from the C++ method to the JavaScript window. How can I achieve this? Below is my code snippet. #include <QtGui/QApplication> #include <Q ...

Guide on dynamically updating a div in PHP with a mix of text output and images at regular intervals

My current project involves creating a browser-based card game primarily using PHP, with potentially incorporating other languages to help me enhance and test my PHP skills. However, I've encountered difficulties while attempting to implement various ...

Revamp the styling of various elements using CSS

Is it possible to combine multiple elements in jQuery to apply a single command rather than individually styling each element? Instead of: $(".element1").css('background','#000'); $(".element2").css('background','#000&ap ...

Gaining entry to a JavaScript prototype method

I'm currently working on a prototype function called event. Prototype Func("input_element_id").event("keyup",function(){ alert("Works on keyup in an Input!"); } Func.prototype= { keyup: function(func){ //adding event listener and c ...

The div element is not expanding as expected, causing the content to overlap

In my code, I have a portfolio-container div with two images and paragraphs inside a list element. The issue I'm facing is that the div is not expanding vertically, causing the images and paragraphs to overlap with the footer below. I've attempte ...

The basic Kendo UI remote DataSource fails to fetch any data

I'm working on a basic exercise where my DataSource object is not returning any data. Here's a snippet of the code: var data = new kendo.data.DataSource({ transport: { read: { url: "data.json", ...

Establish a React component to observe socket.io

I am currently looking for the best way to connect my React component with socket.io in order to listen to events. My current approach involves including the following HTML code: <script src="socket.io/socket.io.js"></script> and then initial ...

Ways to create a responsive design for this specific part of the

https://i.sstatic.net/2JyyN.jpg Hello everyone, I'm looking to make this section responsive at 768px .AppleContent { background-color: #9ACD32; text-align: center; padding: 50px 200px; color: white; } <section class="section-1"& ...

IE9 not triggering jQuery's .load method

I am facing an issue where the load method is not firing on IE9.0 for 4 divs (class=mydiv) with images inside, while it works on other browsers I've tested. I'm unsure if this issue persists in other versions of Internet Explorer. $.noConflict() ...

What is the best way to begin IMA HTML5 SDK ads with sound off?

One challenge I encountered was getting autoplay video to work on iOS 10 using HTML5. To achieve this, I utilized the following code: <video autoplay loop muted playsinline controls> <source src="http://distribution.bbb3d.renderfarming.net/vi ...

Extract values from JSON object and store them in an array, then apply them in an external

I have created an axios JSON data call function and I am looking for a way to extract values from my jJSON function so that they can be used outside of the function. Is there a method to achieve this? Below is my code snippet: let website_names = `${api} ...

The Ajax request functions correctly in Chrome and Safari but encounters issues in Firefox and Internet Explorer

I've encountered an issue with jQuery where the call is failing when making a request with base64 authorization header in the beforeSend function. It's a simple request to retrieve projects. function GetProjects(full){ var query = "/Projects"; $ ...

Comparing react-intl and react-i18next for internationalizing ReactJS applications

I am in the process of developing a multilanguage application using ReactJS. This application will require a custom dictionary for various languages, as well as automatic formatting for date/time, numbers, and currency. After researching, I have come acro ...