Filter data on multiple columns with checkboxes - D3
<p>I'm creating a scatterplot in D3 and I want to use checkboxes to filter the data by two categorical variables. Currently I can filter out data using the checkboxes, but if I re-check a box in one category it will bring back all the data for that category, regardless of what is checked/unchecked in the other category. I'd like to know how best to filter data according to multiple variables at once.</p>

<p>Here's a simplified version of my data and code:</p>

<p>Data (<code>test.csv</code>):</p>

<pre><code>color,texture,length,width
blue,fluffy,4,1
yellow,fluffy,5,2
blue,grainy,3,1
yellow,grainy,4,2
blue,grainy,5,3
yellow,grainy,5,4
</code></pre>

<p>HTML:</p>

<pre><code>&lt;!DOCTYPE html&gt;
&lt;meta charset="utf-8"&gt;
&lt;html lang="en"&gt;
  &lt;body&gt;
    &lt;script src="http://d3js.org/d3.v3.min.js"&gt;&lt;/script&gt;
    &lt;script type="text/javascript" src="test.js"&gt;&lt;/script&gt;

    &lt;!-- Checkboxes --&gt;
    &lt;div class="filter_options"&gt;
        &lt;input class="color_button" id="B" value="blue" type="checkbox"
               checked="checked"&gt;Blue&lt;/input&gt;
        &lt;input class="color_button" id="Y" value="yellow" type="checkbox"
               checked="checked"&gt;Yellow&lt;/input&gt;
    &lt;/div&gt;
    &lt;div class="filter_options"&gt;
        &lt;input class="texture_button" id="F" value="fluffy" type="checkbox"
               checked="checked"&gt;Fluffy&lt;/input&gt;
        &lt;input class="texture_button" id="G" value="grainy" type="checkbox"
               checked="checked"&gt;Grainy&lt;/input&gt;
    &lt;/div&gt;
  &lt;/body&gt;
 &lt;/html&gt;
</code></pre>

<p>Javascript (<code>test.js</code>; filtering happens at the bottom):</p>

<pre><code>// Set width/height/margins
var margin = {top: 20, right: 20, bottom: 30, left: 50};
var w = 900 - margin.left - margin.right;
var h = 500 - margin.top - margin.bottom;

// Create SVG
var svg = d3.select("body").append("svg")
        .attr("width", w + margin.left + margin.right)
        .attr("height", h + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


// x and y scales 
var x = d3.scale.linear()
        .domain([0, 10])
        .range([0, w]);

var y = d3.scale.linear()
        .domain([0, 10])
        .range([h, 0]);

// x axis
var xAxis = d3.svg.axis()
        .ticks(6)
        .scale(x);

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

// Colors for points
var col = d3.scale.ordinal()
    .domain(["blue", "yellow"])
    .range(["#3b80e8", "#ecea5f"]);

// Add x axis ("Length")
svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(0," + h + ")")
    .call(xAxis)
    .append("text")
    .attr("x", w)
    .attr("y", -6)
    .style("text-anchor", "end")
    .text("Length");

// Add y axis ("Width")
svg.append("g")
    .attr("class", "axis")
    .call(yAxis)
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Width");

// Data
var dat;
d3.csv("test.csv", function(data) {
    data.forEach(function(d) {
        d.length = +d.length;
        d.width = +d.width;
    });

    // Scatterplot
    svg.selectAll(".dot")
        .data(data)
        .enter()
        .append("circle")
        .attr("class", "dot")
        .attr("cx", function(d) {return x(d.length); })
        .attr("cy", function(d) {return y(d.width); })
        .attr("r", 7)
        .style("fill", function(d) { return col(d.color); });

    // Filter data by color
    d3.selectAll(".color_button").on("change", function() {
        var selected = this.value,
            display = this.checked ? "inline" : "none";

        svg.selectAll(".dot")
            .filter(function(d) { return selected == d.color; })
            .attr("display", display);
    });

    // Filter data by texture
    d3.selectAll(".texture_button").on("change", function() {
        var selected = this.value,
            display = this.checked ? "inline" : "none";

        svg.selectAll(".dot")
            .filter(function(d) { return selected == d.texture; })
            .attr("display", display);
    });

});
</code></pre>

<p>In the resulting graph, if I uncheck "blue", all the blue points disappear. If I uncheck "fluffy", one more point disappears. If I then re-check "fluffy", I get back the one yellow fluffy point as well as the one blue fluffy point. I think I can see why this is happening with my code as I've written it: since when I re-check "fluffy" I'm not changing changing the state of the color checkboxes, my data isn't getting filtered according to color. I'm brand new to D3 and Javascript, so I don't know how to do better accomplish filtering on multiple columns at the same time.</p>