solub
solub

Reputation: 1363

How to fix this toggle function in d3?

I have 2 problems here:

1/ These buttons (you can run the code snippet below) should reverse back to their former opacity at the second click but they don't ! Can someone explain me what's wrong with the code ?

2/ How do I reverse back to the text former color on the second click without having to define that former color (again!) in the toggle function. ?

Explanation: As you can see when running the following snippet, the text (1,2,3...) turns white at the first click. I'd like to set it back to its former color at the second click without having to write:

var newTextFill = active ? "white" : "**former_color**";

For instance, in this example the author doesn't specify the font-size the legend should return to on a second click and yet you can see it shrinking back to its original size. How can I do the same thing here with the font colors ?

 var name = ["123456789"];
 
 
 var color = d3.scaleOrdinal()
               .range(["#00baff", "#0014fe", "#00dbaf", "#f4bf02", "#ffa600",                        "#ff0000", "#ff00c4", "#ee693e", "#99958f"]);
 
 
 var svg = d3.select("body")
             .append("svg")
             .attr("width", 300)
             .attr("height",300)
 
 var legende = svg.selectAll("bar")
                  .data(name)
                  .enter()
                  .append("g")
                  .attr("x",  0)
                  .attr("y", function (d,i) {return 12 + i * 20})

    legende.append("text")
           .text(function(d,i) { return name[i]})
           .attr("class", "legtext")
           .style("font-family", "helvetica")
           .style("fill-opacity", 0.8)
           .attr("x",  4)
           .attr("y", function (d,i) {return 12 + i * 20})
           .style("font-size", 10)
           .style("fill", function(d,i) { return color(i)})
           .style("text-decoration", "none")
           .style("opacity", 1);

    legende.append("rect")
           .attr("class", "recta")
           .attr("x",  2)
           .attr("y", function (d,i) {return 1 + i * 20})
           .attr("width", 65)
           .attr("height", 15)
           .style("fill", function(d,i) { return color(i)})
           .style("opacity", 0.09);

    legende.append("rect")
           .attr("class", "rectastroke")
           .attr("id", function(d,i) {return "str" + i})
           .attr("x",  2)
           .attr("y", function (d,i) {return 1 + i * 20})
           .attr("width", 65)
           .attr("height", 15)
           .style("stroke", function(d,i) { return color(i)})
           .style("stroke-width", 0.5) 
           .style("fill", "none")
           .attr("display", "none");
           
           
           
    legende.on("click", function (d,i) 
               {var active = d.active ? false : true;
                var newOpacity = active ? 1 : 0.1;
                var newTextFill = active ? "white" : "blue";
               
               d3.select(this)
                 .select("rect")
                 .style("opacity", newOpacity)
                 
                 
               d3.select(this)
                 .select("text")
                 .style("fill", newTextFill)
                 .raise()
                 
               d.active = active  });  
           
           
           
           
    legende.on("mouseover", function(d, i)      
        {d3.select(this).selectAll(".rectastroke").attr("display", "true")});
        
    legende.on("mouseout", function(d, i)      
        {d3.select(this).selectAll(".rectastroke").attr("display", "none")});    
body {font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 300;
          fill: #242424;
          text-align: center;
          cursor: default;}
          
     .legende {cursor: pointer;}
     .recta {cursor: pointer;}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>


  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

   

<body>
     <div class="colored buttons"></div>
</body>
</html>

Upvotes: 1

Views: 1158

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102218

In the bl.ocks you linked she is, in fact, specifying the font size the legend will return to on a second click... in the CSS.

What's happening is that if active is false, the setter will return undefined...

d3.select(this)
    .style("font-size", function() {
        if (active) {return "25px"}
    })

... and the UA will set the style based on the CSS.

You can do the same thing to your rectangles without specifying any newOpacity:

d3.select(this)
    .select("rect")
    .style("opacity", function() {
        if (active) return 1;
    })

Here is the demo:

var data = "123456789".split("").map(function(d) {
  return {
    value: d
  }
});

var color = d3.scaleOrdinal()
  .range(["#00baff", "#0014fe", "#00dbaf", "#f4bf02", "#ffa600", "#ff0000", "#ff00c4", "#ee693e", "#99958f"]);

var svg = d3.select("body")
  .append("svg")
  .attr("width", 300)
  .attr("height", 300)

var legende = svg.selectAll("bar")
  .data(data)
  .enter()
  .append("g")
  .attr("x", 0)
  .attr("y", function(d, i) {
    return 12 + i * 20
  })

legende.append("text")
  .text(function(d, i) {
    return d.value
  })
  .attr("class", "legtext")
  .style("font-family", "helvetica")
  .style("fill-opacity", 0.8)
  .attr("x", 4)
  .attr("y", function(d, i) {
    return 12 + i * 20
  })
  .style("font-size", 10)
  .style("fill", function(d, i) {
    return color(i)
  })
  .style("text-decoration", "none")
  .style("opacity", 1);

legende.append("rect")
  .attr("class", "recta")
  .attr("x", 2)
  .attr("y", function(d, i) {
    return 1 + i * 20
  })
  .attr("width", 65)
  .attr("height", 15)
  .style("fill", function(d, i) {
    return color(i)
  });

legende.on("click", function(d, i) {
  var active = d.active ? false : true;

  d3.select(this)
    .select("rect")
    .style("opacity", function() {
      if (active) return 1;
    })

  d3.select(this)
    .select("text")
    .style("fill", function(e, j) {
      if (active) {
        return "white";
      } else {
        return color(i)
      }
    })
    .raise()

  d.active = active
});
.legende {
  cursor: pointer;
}

.recta {
  opacity: 0.1;
  cursor: pointer;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

For the text color, however, since you are not specifying it in the CSS, you'll have to use a normal if...else code.

Upvotes: 2

Related Questions