Home > Net >  How to change pattern of one bar of a bar chart in d3.js?
How to change pattern of one bar of a bar chart in d3.js?

Time:06-15

I created a bar chart and used patterns to fill it. Now I hope to change the second bar's pattern to a new pattern by clicking the "Change Pattern" button, as shown in the diagram.

I know recreating the whole bar chart can be a solution. But I was wondering whether there is a method only to select the target bar and change its pattern. Thank you in advance.

My code:

Diagram of the result after clicking the button

<!doctype html>
<html>
<head>
    <style>
    </style>
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
  <button id="patternBtn">Change Pattern </button>
<svg id = "barchart" width="600" height="500"></svg>

<svg width="600" height="500">
    
    <defs>
        <pattern id="pattern1"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <circle cx="10" cy="10" r="10" style="stroke: none; fill: #0000ff" />
      
        </pattern>
      </defs>
    
      <defs>
        <pattern id="pattern2"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <circle cx="10" cy="10" r="5" style="stroke: none; fill: #0000ff" />
      
        </pattern>
      </defs>
    
      <defs>
        <pattern id="pattern3"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <circle cx="10" cy="10" r="3" style="stroke: none; fill: #0000ff" />
      
        </pattern>
      </defs>
  
  <defs>
        <pattern id="newPattern"
                 x="5" y="5" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <rect x="0" y="0" width="5" height="5" fill="red"/>
      
        </pattern>
      </defs>
    
        <rect x="0" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern1);" />   
    <rect x="100" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern2);" />   
        <rect x="200" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern3);" />   
  
  <rect x="0" y="100" width="100" height="100"
        style="stroke: #000000; fill: url(#newPattern);" />   
</svg>
</body> 
<script>
  var data = [{ year: '2001', value:10 },
            { year: '2002', value:30 },
            { year: '2003', value:20 },
           ]
var svg = d3.select("#barchart"),
            margin = 200,
            width = svg.attr("width") - margin,
            height = svg.attr("height") - margin


var xScale = d3.scaleBand().range([0, width]).padding(0.4),
            yScale = d3.scaleLinear().range([height, 0]);

var g = svg.append("g")
            .attr("transform", "translate("   100   ","   100   ")");

    

        xScale.domain(data.map(function(d) { return d.year; }));
        yScale.domain([0, d3.max(data, function(d) { return d.value; })]);

        g.append("g")
         .attr("transform", "translate(0,"   height   ")")
         .call(d3.axisBottom(xScale));

        g.append("g")
         .call(d3.axisLeft(yScale).tickFormat(function(d){
             return d;
         }).ticks(10));

        g.selectAll(".bar")
         .data(data)
         .enter().append("rect")
         .attr("class", "bar")
         .attr("x", function(d) { return xScale(d.year); })
         .attr("y", function(d) { return yScale(d.value); })
         .attr("width", xScale.bandwidth())
         .attr("height", function(d) { return height - yScale(d.value); })
         .attr('stroke', "black")
         .attr('stroke-width', '1')
         .attr("fill", function(d,i) { return  "url(#pattern"   (i 1)  ")"});
    

</script>

</html>

CodePudding user response:

Since we want to change only css of the second bar on click, this should be done by assigning different class to second bar, applying the function on class attribute. Once different class is applied, on click capture the bar with that class and changes the pattern. See below on how it is done. I have kept a counter to change pattern on each click, you can change based on what you want

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!doctype html>
<html>

<head>
  <style>

  </style>
  <script src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
  <button id="patternBtn">Change Pattern </button>
  <svg id="barchart" width="600" height="500"></svg>

  <svg width="600" height="500">
    
    <defs>
        <pattern id="pattern1"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <circle cx="10" cy="10" r="10" style="stroke: none; fill: #0000ff" />
      
        </pattern>
      </defs>
    
      <defs>
        <pattern id="pattern2"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <circle cx="10" cy="10" r="5" style="stroke: none; fill: #0000ff" />
      
        </pattern>
      </defs>
    
      <defs>
        <pattern id="pattern3"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <circle cx="10" cy="10" r="3" style="stroke: none; fill: #0000ff" />
      
        </pattern>
      </defs>
  
  <defs>
        <pattern id="pattern4"
                 x="5" y="5" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <rect x="0" y="0" width="5" height="5" fill="red"/>
      
        </pattern>
      </defs>
    
        <rect x="0" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern1);" />   
    <rect x="100" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern2);" />   
        <rect x="200" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern3);" />   
  
  <rect x="0" y="100" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern4);" />   
</svg>
</body>
<script>
  var patternCount = 1;
  var data = [{
      year: '2001',
      value: 10
    },
    {
      year: '2002',
      value: 30
    },
    {
      year: '2003',
      value: 20
    },
  ]
  var svg = d3.select("#barchart"),
    margin = 200,
    width = svg.attr("width") - margin,
    height = svg.attr("height") - margin


  var xScale = d3.scaleBand().range([0, width]).padding(0.4),
    yScale = d3.scaleLinear().range([height, 0]);

  var g = svg.append("g")
    .attr("transform", "translate("   100   ","   100   ")");



  xScale.domain(data.map(function(d) {
    return d.year;
  }));
  yScale.domain([0, d3.max(data, function(d) {
    return d.value;
  })]);

  g.append("g")
    .attr("transform", "translate(0,"   height   ")")
    .call(d3.axisBottom(xScale));

  g.append("g")
    .call(d3.axisLeft(yScale).tickFormat(function(d) {
      return d;
    }).ticks(10));

  g.selectAll(".bar")
    .data(data)
    .enter().append("rect")
    .attr("class", function(d, index) {
      return "bar"   index
    })
    .attr("x", function(d) {
      return xScale(d.year);
    })
    .attr("y", function(d) {
      return yScale(d.value);
    })
    .attr("width", xScale.bandwidth())
    .attr("height", function(d) {
      return height - yScale(d.value);
    })
    .attr('stroke', "black")
    .attr('stroke-width', '1')
    .attr("fill", function(d, i) {
      return "url(#pattern"   (i   1)   ")"
    });

  $('#patternBtn').on("click", function() {
       d3.select(".bar1")
      .attr("fill", `url(#pattern${patternCount})`);
       patternCount  ;
       if (patternCount > 4) patternCount = 1;
  });
</script>

</html>

  • Related