oymonk
oymonk

Reputation: 353

D3 chart with search box

I have created a chart in D3 where nodes show the time at which particular individuals created a document. The chart also presents a search box, which turns nodes red depending on whether the search box input matches the words associated to that document (these words are listed in column 5 of the dataset - please see below for dataset).

enter image description here

My problem: once a search has been input into the box (ie. 'fish'), the website visitor must hit 'refresh' in order to run a new search (ie. 'dog'). I would prefer that instead, the chart remains visible when a new search is initiated, and the 'try it' button merely switches the colours of the nodes to reflect the new search choice. Here's what I have so far:

html,
body {
  margin: 0;
  height: 100%;
}

g.hidden line,
g.hidden path {
  display: none;
}

path {
  stroke: navy;
  stroke-width: 2px;
  fill: none;
}

circle {
  fill: #FF00FF;
  stroke: navy;
  stroke-width: 2px;
}

g.tick text y {
  font-size: 30px;
  font: Garamond;
}

g.tick text x {
  font-size: 10px;
  font: Garamond;
}

g.tick line {
  display: none;

<!-- begin snippet: js hide: false console: true babel: false -->
<!DOCTYPE html>
<html lang="en">

<head>
  <meta chartset="utf-8">
  <title>Interactive scatterplot</title>
  <link rel="stylesheet" type="text/css" href="style.css">
  <script type="text/javascript" src="d3.v4.js"></script>

</head>

<body>

  <script>
    var txtName = 0;

    function sayHi() {
      var txtName = document.getElementById("txtName");
      console.log(txtName.value);



      var parseDate = d3.timeParse("%m/%d/%Y");

      d3.csv("doc.csv")
        .row(function(d) {
          return {
            creator: d.creator,
            date: parseDate(d.date),
            number: Number(d.number),
            doc: d.doc
          };
        })
        .get(function(error, data) {
          var height = 300;
          var width = 500;

          var minDate = new Date(2000, 1, 1);
          var maxDate = new Date(2011, 1, 1);

          var y = d3.scaleOrdinal()
            .domain(['', 'may', 'milly', 'maggie', 'molly', ''])
            .range([300, 225, 150, 75, 15, 0]);

          var x = d3.scaleTime()
            .domain([minDate, maxDate])
            .range([0, width]);

          var yAxis = d3.axisLeft(y);
          var xAxis = d3.axisBottom(x);

          var svg = d3.select("body").append("svg").attr("height", "100%").attr("width", "100%");

          margin = {
            top: 40,
            right: 50,
            bottom: 0,
            left: 50
          };

          var redBox = svg.append("rect")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
            .attr("y", 0)
            .attr("width", width)
            .attr("height", height)
            .attr("fill", "orange");

          var chartGroup = svg.append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

          svg.selectAll("circle")
            .data(data)
            .enter().append("circle")
            //.filter(function(d) {return d.doc = "milly";})
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
            .attr("cx", function(d) {
              return x(d.date);
            })
            .attr("cy", function(d) {
              return y(d.creator);
            })
            .attr("r", 4)
            .style("fill", function(d) {
              if (d.doc == txtName.value) {
                return "red"
              } else {
                return "black"
              }
            });
          console.log(txtName.value);

          chartGroup.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(d3.axisBottom(x).ticks(14));
          chartGroup.append("g").attr("class", "y axis").call(d3.axisLeft(y).ticks(5));

        });
    }
  </script>

  <textarea id="txtName" name="txt-Name" placeholder="Search for something.."></textarea>
  <button onclick="sayHi()">Try it</button>

  <!-- <div class="test-wrapper"> -->

  <!-- </div><!-- .test-wrapper -->
</body>

</html>

Here is the dataset:

date	number	creator		doc
6/16/2000	3	molly	3	rat
2/25/2002	4	may	2	cat
12/05/2004	3	molly	4	fish
07/06/2006	1	milly	1	dog
09/07/2003	4	may	4	fish
12/10/2001	4	may	3	rat
6/15/2005	2	maggie	3	rat
06/09/2004	1	milly	4	fish
10/05/2005	1	milly	3	rat
10/07/2003	4	may	1	dog
1/19/2009	4	may	2	cat
10/30/2007	1	milly	4	fish
8/13/2009	4	may	2	cat
9/30/2004	3	molly	1	dog
1/17/2006	4	may	3	rat
12/18/2009	3	molly	1	dog
11/02/2007	2	maggie	3	rat
4/17/2007	1	milly	4	fish

Currently, the creation of the graph and the process of obtaining the search-box value fall under one function. I tried to make them separate, but ending the function before the creation of the graph meant that the graph did not recognize the variable txtName. I also tried copy-pasting the block relating to the colour of the circles to the sayHi function, but this makes the graph unresponsive.

Any help appreciated.

Upvotes: 4

Views: 3417

Answers (2)

Aditya
Aditya

Reputation: 1377

You can store the circles to a variable and just change the fill based on the textarea value.

I've used a bit of jQuery so that the user doesn't even have to press the Try it button as a bonus. You may remove it if you like.

Here's a plunker

Upvotes: 1

Gerardo Furtado
Gerardo Furtado

Reputation: 102194

Right now you're drawing the chart after the button is clicked. That's not a good idea for changing the colours of the circles when the user clicks the button.

So, remove that inline function call and the sayHi function altogether. Then, inside the d3.tsv callback (that's a TSV, not a CSV), get the value of the text area:

d3.select("button").on("click", function() {
  var txtName = d3.select("#txtName").node().value;
  circles.style("fill", function(d) {
    return d.doc === txtName ? "red" : "black";
  })
})

Here, circles is your circles' selection. Remember to name your selections.

Here is a demo using your code: https://jsfiddle.net/dhy4yt2z/1/

PS: your code have several other minor issues that should be corrected, like the use of margins.

Upvotes: 2

Related Questions