Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looking for a better way to alter the color of axis components in d3.js v4

Tags:

d3.js

I want to change the colour of an axis for a simple d3.js (v4) graph. The y axis in the following image is an example of the finished product;

enter image description here

I suspect that the method I am using to achieve this a bit ugly and I believe that there should be an alternative per the description below that I do not yet understand.

The axis component is a combination of text, path and line elements which require alteration of their respective styles (stroke and fill).

At this point the only method I have found to change the colour of the components is by individually setting styles in the <style> section of the code...

.axisRed line{
  stroke: red;
}

.axisRed path{
  stroke: red;
}

.axisRed text{
  fill: red;
}

...and applying that class to the y axis when appending it later in the JavaScript;

  svg.append("g")
      .attr("class", "axisRed")
      .call(d3.axisLeft(y));

Is there a way that I can apply these styles via .style("<some style>", "<some value>") lines while appending the y axis rather than declaring in the <style> section?

An example with code is here.

I have tried trying to address the individual DOM components such as the 'domain' class, but without success. I suspect that I do not understand the hierarchy of the axis component well enough.

like image 576
d3noob Avatar asked Jul 16 '16 19:07

d3noob


3 Answers

On D3 you have several options to style the elements:

Option A: Using style tag with your class:

axisRed line{
  stroke: red;
}

.axisRed path{
  stroke: red;
}

.axisRed text{
  fill: red;
}
// apply applying that class to the y
 svg.append("g")
      .attr("class", "axisRed")
      .call(d3.axisLeft(y));

Option B: Using style tag with auto-generate class:

D3 assign domain class for the axis line, and inside a group 'g' for every tick class tick line and text. Without define a particular class:

<style>
    path.domain  {
    stroke: red;
}
   .tick text {
    fill:blue
}
</style
.
.
 svg.append("g").call(d3.axisLeft(y));

// generate code 
<path class="domain" stroke="#000" d="M-6,450.5H0.5V0.5H-6"></path>
<g class="tick" opacity="1" transform="translate(0,414)">
      <line stroke="#000" x2="-6" y1="0.5" y2="0.5"></line>
      <text fill="#000" x="-9" y="0.5" dy=".32em">50</text>
</g>

Option C:Inline at appening time

On D3 V3 you could do a more compact:

svg.append("line")
   .style({
      fill:"none",
      stroke:"#f00",
      "stroke-width":"10"
    })

But on D3 V4, doesn't work. So I open a 'Issue': you can check and follow the progress here: https://github.com/d3/d3-selection/issues/82

** UPDATE ** On V4 you must include selection-multi:

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v0.4.min.js"></script>

and then use: styles intead style and attrs instead attr:

svg.append("line")
   .styles({
      fill:"none",
      stroke:"#f00",
      "stroke-width":"10"
    })

for your example:

svg.selectAll(".domain")
   .styles({ fill:"none", stroke:"#f00",  "stroke-width":"1" });

Now you got a compact way to style your elements

Extra: you could style and attr any elements:

 var svg = d3.select("body").append("svg")
        .attrs({width:600, height:600})
        .styles({
             border:"1px",
            "border-style":"solid",
            "border-color":"#f00"})

Here's the plunker

like image 123
Klaujesi Avatar answered Oct 21 '22 01:10

Klaujesi


To style them inline, just keep a reference to the added axis and select the components:

  // Add the Y Axis
  var axis = svg.append("g")
    .call(d3.axisLeft(y));

  axis.selectAll("line")
    .style("stroke", "purple");

  axis.selectAll("path")
    .style("stroke", "green");

  axis.selectAll("text")
    .style("stroke", "blue");

Full example:

<!DOCTYPE html>
<meta charset="utf-8">

<body>

  <!-- load the d3.js library -->
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script>
    // set the dimensions and margins of the graph
    var margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 50
      },
      width = 960 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    // parse the date / time
    var parseTime = d3.timeParse("%d-%b-%y");

    // set the ranges
    var x = d3.scaleTime().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);

    // define the line
    var valueline = d3.line()
      .x(function(d) {
        return x(d.date);
      })
      .y(function(d) {
        return y(d.close);
      });

    // append the svg obgect to the body of the page
    // appends a 'group' element to 'svg'
    // moves the 'group' element to the top left margin
    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 + ")");

    var data = [{
      "date": "1-May-12",
      "close": "58.13"
    }, {
      "date": "30-Apr-12",
      "close": "53.98"
    }, {
      "date": "27-Apr-12",
      "close": "67.00"
    }, {
      "date": "26-Apr-12",
      "close": "89.70"
    }, {
      "date": "25-Apr-12",
      "close": "99.00"
    }, {
      "date": "24-Apr-12",
      "close": "130.28"
    }, {
      "date": "23-Apr-12",
      "close": "166.70"
    }, {
      "date": "20-Apr-12",
      "close": "234.98"
    }, {
      "date": "19-Apr-12",
      "close": "345.44"
    }, {
      "date": "18-Apr-12",
      "close": "443.34"
    }, {
      "date": "17-Apr-12",
      "close": "543.70"
    }, {
      "date": "16-Apr-12",
      "close": "580.13"
    }, {
      "date": "13-Apr-12",
      "close": "605.23"
    }, {
      "date": "12-Apr-12",
      "close": "622.77"
    }, {
      "date": "11-Apr-12",
      "close": "626.20"
    }, {
      "date": "10-Apr-12",
      "close": "628.44"
    }, {
      "date": "9-Apr-12",
      "close": "636.23"
    }, {
      "date": "5-Apr-12",
      "close": "633.68"
    }, {
      "date": "4-Apr-12",
      "close": "624.31"
    }, {
      "date": "3-Apr-12",
      "close": "629.32"
    }, {
      "date": "2-Apr-12",
      "close": "618.63"
    }, {
      "date": "30-Mar-12",
      "close": "599.55"
    }, {
      "date": "29-Mar-12",
      "close": "609.86"
    }, {
      "date": "28-Mar-12",
      "close": "617.62"
    }, {
      "date": "27-Mar-12",
      "close": "614.48"
    }, {
      "date": "26-Mar-12",
      "close": "606.98"
    }];

    // format the data
    data.forEach(function(d) {
      d.date = parseTime(d.date);
      d.close = +d.close;
    });

    // 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.close;
    })]);

    // Add the valueline path.
    svg.append("path")
      .data([data])
      .attr("class", "line")
      .attr("d", valueline)
      .style("fill", "none")
      .style("stroke", "steelblue");

    // Add the X Axis
    svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

    // Add the Y Axis
    var axis = svg.append("g")
      .call(d3.axisLeft(y));

    axis.selectAll("line")
      .style("stroke", "purple")

    axis.selectAll("path")
      .style("stroke", "green")

    axis.selectAll("text")
      .style("stroke", "blue")
  </script>
</body>
like image 42
Mark Avatar answered Oct 21 '22 03:10

Mark


For v5 you can use below code, Select domain and define stroke for which axis you need and append to svg.

 yAxis = (g) =>
          g
            .attr("transform", `translate(${50} ${0})`)
            .call((g) =>
              g
                .selectAll(".domain")
                .attr("stroke-width", 0.3)
                .attr("stroke", d3.schemePaired[7])
            );

  svg.append("g").call(yAxis);
      
like image 43
Shibin Raju Mathew Avatar answered Oct 21 '22 03:10

Shibin Raju Mathew