Home > Mobile >  Function not working after placing D3 graph in a dropdown select list
Function not working after placing D3 graph in a dropdown select list

Time:11-16

I have a set of D3js arc graphs that were working fine until I made them viewable only if selected using a menu. The function which wraps the text on the labels, entitled wrap() is working on the graph that is visible when you load the page, but now the rest aren't calling the wrap() function so the label text is way too wide. Do I call the wrap() function in my function that is used to select the graphs, or is my syntax incorrect?

In the interest of providing less code I have removed everything pertaining to the graphs that aren't the first two. I have tried calling the wrap() function in an onclick with one of the option buttons on the select menu, as well as including it in the if/else statement, neither of which worked. Here is the fiddle and the code, thanks in advance!

https://jsfiddle.net/arkatark/ygakxn4z/3/


<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

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

    <style>
      text {
        font-family: Gotham Narrow, sans-serif;
      }

      .intro {
        font-family: Gotham Narrow, sans-serif;
        font-size: 2em;
        max-width: 700px;
        text-align: center;
        margin-left: auto;
        margin-right: auto;
      }

      .container {
        position: absolute;
        top: 10%;
        left: 50%;
        margin-top: -50px;
        margin-left: -50px;
        width: 590px;
      }

      #graph1822 {
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 100px;
        margin-top: 0px;
        border-style: solid;
        width: 580px;
      }

      #graph1823,
      #graph1824,
      #graph1825,
      #graph1826,
      #graph1827 {
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 100px;
        margin-top: 0px;
        border-style: solid;
        display: none;
        width: 580px;
      }

      #my_dataviz {
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 100px;
        margin-top: 0px;
      }

      label {
        font-family: Gotham Narrow, sans-serif;
      }

      select {
        width: 55px;
        margin-bottom: 10px;

      }

    </style>

  </head>

  <body>
    <div >
      <div >
        <h4 >
          Arc Diagram of Colonial Secretaries' Correspondence
        </h4>
      </div>
      
      <div >
        <div id="my_dataviz"></div>

        <label for="dropdown">Choose a year:</label>
        <select id="dropdown">
          <option value="0">1822</option>
          <option value="1">1823</option>
          <option value="2">1824</option>
          <option value="3">1825</option>
          <option value="4">1826</option>
          <option value="5">1827</option>
        </select>


        <div id="graph1822"></div>
        <div id="graph1823"></div>
        <div id="graph1824"></div>
        <div id="graph1825"></div>
        <div id="graph1826"></div>
        <div id="graph1827"></div>
      </div>
    </div>
    <script>
      // set the dimensions and margins of the graph


      var el = document.getElementById("dropdown");
      el.addEventListener("change", function() {
        var elems = document.querySelectorAll('#graph1822, #graph1823 ,#graph1824 ,#graph1825, #graph1826, #graph1827')
        for (var i = 0; i < elems.length; i  ) {
          elems[i].style.display = 'none'
        }
        if (this.selectedIndex === 0) {
          document.querySelector('#graph1822').style.display = 'block';
          document.querySelector('#dropdown').style.outlineColor = '#E9D8A6';
        } else if (this.selectedIndex === 1) {

          document.querySelector('#graph1823').style.display = 'block';
          document.querySelector('#graph1822').style.display = 'none';
          document.querySelector('#dropdown').style.outlineColor = '#E9B006';
        } else if (this.selectedIndex === 2) {
          document.querySelector('#graph1824').style.display = 'block';
          document.querySelector('#graph1822').style.display = 'none';
          document.querySelector('#dropdown').style.outlineColor = '#BB3E03';
        } else if (this.selectedIndex === 3) {
          document.querySelector('#graph1825').style.display = 'block';
          document.querySelector('#graph1822').style.display = 'none';
          document.querySelector('#dropdown').style.outlineColor = '#005F73';

        } else if (this.selectedIndex === 4) {
          document.querySelector('#graph1825').style.display = 'block';
          document.querySelector('#graph1822').style.display = 'none';
          document.querySelector('#dropdown').style.outlineColor = '#0A9396';
        } else if (this.selectedIndex === 5) {
          document.querySelector('#graph1825').style.display = 'block';
          document.querySelector('#graph1822').style.display = 'none';
          document.querySelector('#dropdown').style.outlineColor = '#BCE6E6';
        }
      }, false);







      // DIAGRAM 1822 //

      (function() {
        var margin = {
            top: 30,
            right: 60,
            bottom: 30,
            left: 60
          },
          width = 580 - margin.left - margin.right,
          height = 400 - margin.top - margin.bottom;

        // append the svg object to the body of the page
        var svg = d3.select("#graph1822")
          .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   ")")

        // Read data
        d3.json("https://raw.githubusercontent.com/arkatark/colsec/main/nodes.json", function(data) {

          // List of node names
          var allNodes = data.nodes1.map(function(d) {
            return d.name
          })

          // A linear scale to position the nodes on the X axis
          var x = d3.scalePoint()
            .range([0, width])
            .domain(allNodes)


          var color = d3.scaleOrdinal().domain([1, 10])
            .range(["#E9D8A6", "#E9B006", "#E9B006", "#BB3E03", "#005F73", "#0A9396", "#BCE6E6"]);

          function wrap(text, width) {
            text.each(function() {
              var text = d3.select(this),
                words = text.text().split(/\s /).reverse(),
                word,
                line = [],
                lineNumber = 0,
                lineHeight = 1.1, // ems
                x = text.attr("x"),
                y = text.attr("y"),
                dy = 0, //parseFloat(text.attr("dy")),
                tspan = text.text(null)
                .append("tspan")
                .attr("x", x)
                .attr("y", y)
                .attr("dy", dy   "em");
              while (word = words.pop()) {
                line.push(word);
                tspan.text(line.join(" "));
                if (tspan.node().getComputedTextLength() > width) {
                  line.pop();
                  tspan.text(line.join(" "));
                  line = [word];
                  tspan = text.append("tspan")
                    .attr("x", x)
                    .attr("y", y)
                    .attr("dy",   lineNumber * lineHeight   dy   "em")
                    .text(word);
                }
              }
            });
          }

          // Add the circle for the nodes
          var nodes = svg
            .selectAll("mynodes")
            .data(data.nodes1)
            .enter()
            .append("circle")
            .attr("cx", function(d) {
              return (x(d.name))
            })
            .attr("cy", height - 30)
            .attr("r", 10)
            .style("fill", function(d) {
              return color(d.grp)
            })


          // And give them a label
          var labels = svg
            .selectAll("mylabels")
            .data(data.nodes1)
            .enter()
            .append("text")
            .attr("x", function(d) {
              return (x(d.name))
            })
            .attr("y", height - 10)
            .text(function(d) {
              return (d.name)
            })
            .style("text-anchor", "middle")
            .style("font-size", "10")
            .call(wrap, 100);

          // Add links between nodes. Here is the tricky part.
          // In my input data, links are provided between nodes -id-, NOT between node names.
          // So I have to do a link between this id and the name
          var idToNode = {};
          data.nodes.forEach(function(n) {
            idToNode[n.id] = n;
          });
          // Cool, now if I do idToNode["2"].name I've got the name of the node with id 2

          // Add the links
          var links = svg
            .selectAll('mylinks')
            .data(data.links1)
            .enter()
            .append('path')
            .attr('d', function(d) {
              start = x(idToNode[d.source].name) // X position of start node on the X axis
              end = x(idToNode[d.target].name) // X position of end node
              return ['M', start, height - 30, // the arc starts at the coordinate x=start, y=height-30 (where the starting node is)
                  'A', // This means we're gonna build an elliptical arc
                  (start - end) / 1.95, ',', // Next 2 lines are the coordinates of the inflexion point. Height of this point is proportional with start - end distance
                  (start - end) / 2, 0, 0, ',',
                  start < end ? 1 : 0, end, ',', height - 30
                ] // We always want the arc on top. So if end is before start, putting 0 here turn the arc upside down.
                .join(' ');
            })
            .style("fill", "none")
            .attr("stroke", "#919191")

          var tooltip1 = d3.select("#graph1822")
            .append("div")
            .style("opacity", 0)
            .attr("class", "tooltip")
            .style("background-color", "#FFFFFF")
            .style("border-style", "solid")
            .style("border-width", "1px")
            .style("position", "absolute")
            .style('width', '200px')

          // Add the highlighting functionality
          nodes
            .on('mouseover', function(d) {
              // Highlight the nodes: every node is green except of him
              nodes.style('fill', function(d) {
                return color(d.grp)
              })
              d3.select(this).style('fill', function(d) {
                return color(d.grp)
              })
              // Highlight the connections
              links
                .style('stroke', function(link_d) {
                  return link_d.source === d.id || link_d.target === d.id ? color(d.grp) : '#b8b8b8';
                })
                .style('stroke-width', function(link_d) {
                  return link_d.source === d.id || link_d.target === d.id ? 4 : 1;
                })

              tooltip1
                .html(d.name)
                .style('text-align', 'center')
                .style('font-family', 'Gotham Narrow, sans-serif')
                .style('font-size', '.9em')
                .style('margin-left', '29%')
                .style('margin-right', '30%')
                .style("opacity", .8)
                .style("stroke", "#EEEEEE")
                .style("background-color", "#FFFFFF")
                .style("z-index", 20)
                .style('padding', '10px')

            })
            .on('mouseout', function(d) {
              nodes.attr("r", '8').style("fill", function(d) {
                return color(d.grp)
              })
              links
                .style('stroke', '#919191')
                .style('stroke-opacity', 1)
                .style('stroke-width', '1')
              tooltip1
                .style("opacity", 0)
              d3.select(this)
                .style("stroke", "none")
                .style("opacity", 1)
            })
        })

        svg
          .append("text")
          .attr("text-anchor", "middle")
          .style("fill", "#black")
          .style("font-size", "17px")
          .attr("x", 225)
          .attr("y", 40)
          .html(" Correspondence from 1822")
      })();







      // DIAGRAM 1823 //

      (function() {
        var margin = {
            top: 40,
            right: 60,
            bottom: 30,
            left: 60
          },
          width = 580 - margin.left - margin.right,
          height = 400 - margin.top - margin.bottom;

        // append the svg object to the body of the page
        var svg = d3.select("#graph1823")
          .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   ")");

        // Read data
        d3.json("https://raw.githubusercontent.com/arkatark/colsec/main/nodes.json", function(data) {

          // List of node names
          var allNodes = data.nodes2.map(function(d) {
            return d.name
          })

          // A linear scale to position the nodes on the X axis
          var x = d3.scalePoint()
            .range([0, width])
            .domain(allNodes)

          var color = d3.scaleOrdinal().domain([1, 10])
            .range(["#E9D8A6", "#E9B006", "#E9B006", "#BB3E03", "#005F73", "#0A9396", "#BCE6E6"]);

          function wrap(text, width) {
            text.each(function() {
              var text = d3.select(this),
                words = text.text().split(/\s /).reverse(),
                word,
                line = [],
                lineNumber = 0,
                lineHeight = 1.1, // ems
                x = text.attr("x"),
                y = text.attr("y"),
                dy = 0, //parseFloat(text.attr("dy")),
                tspan = text.text(null)
                .append("tspan")
                .attr("x", x)
                .attr("y", y)
                .attr("dy", dy   "em");
              while (word = words.pop()) {
                line.push(word);
                tspan.text(line.join(" "));
                if (tspan.node().getComputedTextLength() > width) {
                  line.pop();
                  tspan.text(line.join(" "));
                  line = [word];
                  tspan = text.append("tspan")
                    .attr("x", x)
                    .attr("y", y)
                    .attr("dy",   lineNumber * lineHeight   dy   "em")
                    .text(word);
                }
              }
            });
          }

          // Add the circle for the nodes
          var nodes = svg
            .selectAll("mynodes")
            .data(data.nodes2)
            .enter()
            .append("circle")
            .attr("cx", function(d) {
              return (x(d.name))
            })
            .attr("cy", height - 30)
            .attr("r", 8)
            .style("fill", function(d) {
              return color(d.grp)
            })


          // And give them a label
          var labels = svg
            .selectAll("mylabels")
            .data(data.nodes2)
            .enter()
            .append("text")
            .attr("x", function(d) {
              return (x(d.name))
            })
            .attr("y", height - 10)
            .text(function(d) {
              return (d.name)
            })
            .style("text-anchor", "middle")
            .style("font-size", "10")
            .call(wrap, 130);

          // Add links between nodes. Here is the tricky part.
          // In my input data, links are provided between nodes -id-, NOT between node names.
          // So I have to do a link between this id and the name
          var idToNode = {};
          data.nodes.forEach(function(n) {
            idToNode[n.id] = n;
          });
          // Cool, now if I do idToNode["2"].name I've got the name of the node with id 2

          // Add the links
          var links = svg
            .selectAll('mylinks')
            .data(data.links2)
            .enter()
            .append('path')
            .attr('d', function(d) {
              start = x(idToNode[d.source].name) // X position of start node on the X axis
              end = x(idToNode[d.target].name) // X position of end node
              return ['M', start, height - 30, // the arc starts at the coordinate x=start, y=height-30 (where the starting node is)
                  'A', // This means we're gonna build an elliptical arc
                  (start - end) / 1.95, ',', // Next 2 lines are the coordinates of the inflexion point. Height of this point is proportional with start - end distance
                  (start - end) / 2, 0, 0, ',',
                  start < end ? 1 : 0, end, ',', height - 30
                ] // We always want the arc on top. So if end is before start, putting 0 here turn the arc upside down.
                .join(' ');
            })
            .style("fill", "none")
            .attr("stroke", "#919191")

          var tooltip1 = d3.select("#graph1823")
            .append("div")
            .style("opacity", 0)
            .attr("class", "tooltip")
            .style("background-color", "#FFFFFF")
            .style("border-style", "solid")
            .style("border-width", "1px")
            .style("position", "absolute")
            .style('width', '200px')

          // Add the highlighting functionality
          nodes
            .on('mouseover', function(d) {
              // Highlight the nodes: every node is green except of him
              nodes.style('fill', function(d) {
                return color(d.grp)
              })
              d3.select(this).style('fill', function(d) {
                return color(d.grp)
              })
              // Highlight the connections
              links
                .style('stroke', function(link_d) {
                  return link_d.source === d.id || link_d.target === d.id ? color(d.grp) : '#b8b8b8';
                })
                .style('stroke-width', function(link_d) {
                  return link_d.source === d.id || link_d.target === d.id ? 4 : 1;
                })

              tooltip1
                .html(d.name)
                .style('text-align', 'center')
                .style('font-family', 'Gotham Narrow, sans-serif')
                .style('font-size', '.9em')
                .style('margin-left', '30%')
                .style('margin-right', '30%')
                .style("opacity", .8)
                .style("stroke", "#EEEEEE")
                .style("background-color", "#FFFFFF")
                .style("z-index", 20)
                .style('padding', '10px')

            })
            .on('mouseout', function(d) {
              nodes.attr("r", '8').style("fill", function(d) {
                return color(d.grp)
              })
              links
                .style('stroke', '#919191')
                .style('stroke-opacity', 1)
                .style('stroke-width', '1')
              tooltip1
                .style("opacity", 0)
              d3.select(this)
                .style("stroke", "none")
                .style("opacity", 1)
            })
        })

        svg
          .append("text")
          .attr("text-anchor", "middle")
          .style("fill", "#black")
          .style("font-size", "17px")
          .attr("x", 225)
          .attr("y", 40)
          .html(" Correspondence from 1823")
      })();

    </script>
  </body>

</html>

CodePudding user response:

The problem is that the getComputedTextLength() function cannot be used to determine the length of the text on a element not rendered (display: none). So the wrap() function does not work on the graphs not displayed initially.

Perhaps you could render a graph only when it is selected in the dropdown ? This will also avoid duplication in your code.

  • Related