Enhancing D3 visualization with centralized alignment and mobile-friendly responsiveness

I am still quite new to JavaScript and development in general, so I may be overlooking something obvious. Although I have successfully created a chart using d3, I am struggling with positioning. No matter how much I manipulate it with CSS, the chart just doesn't behave as expected. Setting display to block and margins to auto didn't have any effect. The only way I can adjust the positioning is by tweaking the margins in the d3 code, which isn't ideal for responsiveness. I also tried using text-align without success. My goal is to centrally align the chart and have it scale larger as the screen size increases. This should be simple to achieve using CSS, but for some reason, it's not working at all. Any help would be greatly appreciated.

Here is the JavaScript code:

 // Set the dimensions of the canvas / graph
    var margin = {top: 20, right: 0, bottom: 70, left: 70},
        width = 300 - margin.left - margin.right,
        height = 300 - margin.top - margin.bottom;

    // Parse the date / time
    var parseDate = d3.time.format("%-m/%-d/%Y").parse;

    // Set the ranges
    var x = d3.time.scale().range([0, width]);
    var y = d3.scale.linear().range([height, 0]);

    // Define the axes
    var xAxis = d3.svg.axis().scale(x)
        .orient("bottom").ticks(10);

    var yAxis = d3.svg.axis().scale(y)
        .orient("left").ticks(5);

    // chart area fill

    var area = d3.svg.area()
        .x(function(d) { return x(d.Date); })
        .y0(height)
        .y1(function(d) { return y(d.Forecast); });

    // Define the line
    var valueline = d3.svg.line()
        .interpolate("cardinal")
        .x(function(d) { return x(d.Date); })
        .y(function(d) { return y(d.Orders); });

    var valueline2 = d3.svg.line()
        .interpolate("cardinal")
        .x(function(d) { return x(d.Date); })
        .y(function(d) { return y(d.Forecast); });

    // Adds the svg canvas
    var svg = d3.select("body")
        .append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
        .append("g")
            .attr("transform", 
                  "translate(" + margin.left + "," + margin.top + ")");


    // Get the data
    d3.csv("csv/Forecast.csv", function(error, data) {
        data.forEach(function(d) {
            d.Date = parseDate(d.Date);
            d.Orders = +d.Orders;
        });
     // Scale the range of the data
        x.domain(d3.extent(data, function(d) { return d.Date; }));
        y.domain([0, d3.max(data, function(d) { return d.Orders; })]);


        // Area

        svg.append("path")
            .datum(data)
            .attr("class", "area")
            .attr("d", area);

        // Add the valueline path.

        svg.append("path")
            .attr("class", "line")
            .attr("d", valueline2(data))
            .style("stroke", "#A7A9A6");

        svg.append("path")
            .attr("class", "line")
            .attr("d", valueline(data));

        // Add the X Axis
        svg.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis)
            .selectAll("text")
                .style("text-anchor", "end")
                .attr("dx", "-.8em")
                .attr("dy", ".15em")
                .attr("transform", "rotate(-65)");

        // Add the Y Axis
        svg.append("g")
            .attr("class", "y axis")
            .call(yAxis);

    });

Answer №1

JSFIDDLE Responsive and Centered:

To see the responsive version, click here.

For the non-responsive version, click here.

To make your design responsive, utilize the SVG viewBox feature.

Here are some useful links:

Steps for setting up viewBox:

var margin = {top: 100, right: 150, bottom: 100, left: 150}

var outerWidth  = 1600,
    outerHeight = 900;

var width  = outerWidth - margin.right - margin.left,
    height = outerHeight - margin.top - margin.bottom;


d3.select(".plot-div").append("svg")
    .attr("class", "plot-svg")
    .attr("width", "100%")
    .attr("viewBox", "0 0 " + outerWidth + " " + outerHeight)
    .append("g")
      .attr("class", "plot-space")
      .attr("transform", 
        "translate(" + margin.left + "," + margin.top + ")"
      );

Key points from the instructions above:

  1. The SVG is placed inside a div -- adjust the size and position of the div rather than directly altering the SVG.
  2. The SVG width is relative to its parent div, not fixed in pixels.
  3. All elements drawn in SVG relate to outerWidth x outerHeight, with unitless values being rescaled using viewBox.

An example using a rect in the JSFIDDLE demonstration:

d3.select(".plot-svg").append("rect")
    .attr("x", 0)
    .attr("y", 3*outerHeight/4)
    .attr("width", 800)
    .attr("height", outerHeight/4)
    .attr("fill", "grey")

Adjusting window size will maintain the rectangle filling half the SVG area due to correct scaling.

Repositioning and centering:

You can manipulate the containing div to position your SVG/chart as per your requirements. For instance, I have centered it by making it occupy 50% of the page with margin: auto.

.plot-div{
  width: 50%;
  display: block;
  margin: auto;
 }

Ensure that any scaling or positioning applied to the div also affects the chart within.

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

Is file timestamp utilized by Apache to verify if a resource has been changed?

Currently, I am working on an HTML page that references a large JavaScript file (1MB+) that is rarely updated. According to this source, the JavaScript file will not be resent if it hasn't been modified. I'm curious about how Apache determines i ...

The useStarRating() hook continues to display 0 even after the user has interacted with the star component

I've created a custom useStarRating hook to manage the state of a star rating component in my React project. Everything seems to be working properly, but I'm facing an issue with retrieving the updated value of currentValue after the user interac ...

"Using SetState frequently results in multiple rerenders of the component

Currently, I am developing a messenger application with a main screen component that displays all messages. My goal is to make sure that whenever a user sends or receives a message, the component updates the Flatlist to show the latest sent message. To ach ...

Vue Eslint Extension

My current project utilizes the eslint vue plugin with specific rules set in place. "rules": { "vue/html-closing-bracket-newline": ["error", { "singleline": "never", "multiline": "always" }], "vue/html-closi ...

Is there a way to incorporate Vue script in Laravel without utilizing Vue templates?

I have a question that may seem simple, but I'm curious about the best way to use vue script on pages individually without declaring it globally. For example, I have multiple pages in Laravel Blade such as the home page, category page, and product pag ...

What is the best way to send ServerSideProps to a different page in Next.js using TypeScript?

import type { NextPage } from 'next' import Head from 'next/head' import Feed from './components/Feed'; import News from './components/News'; import Link from 'next/link'; import axios from 'axios&apo ...

"Trying to access jQuery .slide and .slideUp features, but unfortunately they are

I've created this script: $("#comments .comment .links").hide(); $("#comments .comment").hover( function() { $(".links", this).stop(true).slideDown(300); }, function() { $(".links", this).stop(true).slideUp(300); } ); However, I'm facin ...

php code to paginate mysql results

Imagine I have 50 rows in my database. How can I retrieve MySQL results in pages, displaying 5 results on each page and showcasing the pages as follows: [1], 2, 3, 4...10? For example, if it's on page 5, show 3, 4, [5], 6, 7...10 without refreshing al ...

Localhost is causing issues with Laravel in retrieving webfonts

I was trying to incorporate font-awesome into my Laravel project, but encountered a strange error. When I run the project, the following error appears in the console: GET http://localhost/fonts/vendor/@fortawesome/fontawesome-free/webfa-solid-900.woff2?5 ...

What are some ways to eliminate spacing between child and parent div elements?

I am facing an issue with a parent div that contains multiple children. I want to add spacing between the children by applying margins, but this also creates unwanted space between the parent and the children. Is there a clean solution to remove this space ...

The integration of Material-UI Autocomplete and TextField causes google autocomplete to activate

I am currently working on integrating the Autocomplete component into my project. However, I am facing an issue where the browser's autofill/autocomplete feature kicks in after some time. Is there a way to disable this behavior? ...

The clearfix feature is ineffective when using AngularJS

<ul class="dropdown-menu wm_search_div" ng-show="searchDivShow"> <li ng-repeat="user in searchUserList"> <a href="javascript:void(0);" class="wm_clearfix3"> <img ng-src="{{user.faceIcon}}" class="pull-left wm_se ...

Error: The JavaScript variable 'undefined' is being used as a function, which is incorrect. This error occurs when trying to execute the function `mockBackend

I am currently working on unit testing an AngularJS controller using Karma and Jasmine. Below is the test suite I have created: describe('Controllers', function(){ var $scope, ctrl; beforeEach(module('curriculumModule')); ...

Tips for using JavaScript to set images from Flickr API as img src

I've been attempting to populate a table with images fetched from flickr. The array I'm using consists of urls like: ["https://www.flickr.com/photos/113081696@N07/24695273486", "https://www.flickr.com/photos/113081696@N07/24565358002", "https:// ...

Execute consecutive Angular2 functions in a sequential manner, one following the next

In my Angular2 project, I have a service that fetches data for dropdown menus on a form. However, when I call this service multiple times with different parameters in the form component initialization, only the last call seems to work, overriding the previ ...

The .prepend() method receives the variable returned by ajax and adds it

I'm facing a challenge with adding a dynamic select box to a string within my .prepend() function. The options in the select box are subject to change, so hard coding them is not an option. To tackle this issue, I am using an AJAX call to construct th ...

Imitate the actions of images with {width: 100%; height : auto} properties

I am interested in creating a unique layout that consists of a stripe composed of images with varying widths and heights. These images need to be proportional, scaled at the same height, and collectively have a width equal to the parent element. However, ...

Handling Removal of Selected Option in React Material-UI Autocomplete Single Selection

I am currently using material UI autocomplete to create a single-select dropdown. However, I have encountered an issue wherein the onChange event does not get triggered when I click the close button on the right side of the input. This prevents my state fr ...

What are the most effective ways to manage state in form components using React?

As I delve into the realm of best practices for managing state in React components, I find myself grappling with different approaches. Initially, I crafted a form by creating a TextField component structured like this: var TextField = React.createClass({ ...

What are the common issues with Angular 2's ng-if directive?

I am completely new to working with Angular and have already gone through all the ng-if related questions without finding a solution that fits my issue. Here is my code: <tr *ngFor="#position of positions"> <td> ...