如何处理单击 D3.js 按钮时的数据过滤?

How can I handle data filtering on a button click on D3.js?

我有一个正在处理的 D3.js 图表,它工作得很好。但是,我正在尝试在主 html 上添加按钮来处理数据过滤器。这些按钮在外部处理。我似乎无法找出处理该功能以使其工作的最佳方法。任何帮助将不胜感激。这是源代码。

在 "Popular," 的点击事件中,我希望显示值大于 5 的节点。在 Marvel.json 中,我的人气值 属性 .当我点击全部时,它应该显示所有数据。

如有任何意见,我们将不胜感激。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Marvel Characters | Force layout with images</title>
    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

    <style>
      @import url(http://fonts.googleapis.com/css?family=Source+Code+Pro:400,600);
      body {font-family: "Source Code Pro", Consolas, monaco, monospace; line-height: 160%; font-size: 16px;  margin: 0; }
      path.link {
        fill: none;
        stroke-width: 2px;
      }
      .node:not(:hover) .nodetext {
        display: none;
      }
      h1 { font-size: 36px; margin: 10px 0; text-transform: uppercase; font-weight: normal;}
      h2, h3 { font-size: 18px; margin: 5px 0 ; font-weight: normal;}
      header {padding: 20px; position: absolute; top: 0; left: 0;}
      a:link { color: #EE3124; text-decoration: none;}
      a:visited { color: #EE3124; }
      a:hover { color: #A4CD39; text-decoration: underline;}
      a:active { color: #EE3124; }
    </style>
</head>


<body>
    <header>
<!-- D3 BUTTONS -->
  <div class="btn-group" role="group">
    <button type="button" class="btn btn-default button1 active">All</button>
    <button type="button" class="btn btn-default button3">Popular</button>
  </div>
      <h1>Marvel Characters</h1>
      <h2>Click to view their identity</h2>
      <h3>And link to their web page!</h3>
    </header>
    <!-- container for force layout visualisation  -->
    <section id="vis"></section> 
<script>

// some colour variables
  var tcBlack = "#130C0E";

// rest of vars
var w = 960,
    h = 800,
    maxNodeSize = 50,
    x_browser = 20,
    y_browser = 25,
    root;

var vis;
var force = d3.layout.force(); 

vis = d3.select("#vis").append("svg").attr("width", w).attr("height", h);

d3.json("marvel.json", function(json) {

  root = json;
  root.fixed = true;
  root.x = w / 2;
  root.y = h / 4;


        // Build the path
  var defs = vis.insert("svg:defs")
      .data(["end"]);


  defs.enter().append("svg:path")
      .attr("d", "M0,-5L10,0L0,5");

     update();
});


/**
 *   
 */
function update() {
  var nodes = flatten(root),
      links = d3.layout.tree().links(nodes);

  // Restart the force layout.
  force.nodes(nodes)
        .links(links)
        .gravity(0.05)
    .charge(-1500)
    .linkDistance(100)
    .friction(0.5)
    .linkStrength(function(l, i) {return 1; })
    .size([w, h])
    .on("tick", tick)
        .start();

   var path = vis.selectAll("path.link")
      .data(links, function(d) { return d.target.id; });

    path.enter().insert("svg:path")
      .attr("class", "link")
      // .attr("marker-end", "url(#end)")
      .style("stroke", "#eee");


  // Exit any old paths.
  path.exit().remove();



  // Update the nodes…
  var node = vis.selectAll("g.node")
      .data(nodes, function(d) { return d.id; });


  // Enter any new nodes.
  var nodeEnter = node.enter().append("svg:g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
      .on("click", click)
      .call(force.drag);

  // Append a circle
  nodeEnter.append("svg:circle")
      .attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; })
      .style("fill", "#eee");


  // Append images
  var images = nodeEnter.append("svg:image")
        .attr("xlink:href",  function(d) { return d.img;})
        .attr("x", function(d) { return -25;})
        .attr("y", function(d) { return -25;})
        .attr("height", 50)
        .attr("width", 50);

  // make the image grow a little on mouse over and add the text details on click
  var setEvents = images
          // Append hero text
          .on( 'click', function (d) {
              d3.select("h1").html(d.hero); 
              d3.select("h2").html(d.name); 
              d3.select("h3").html ("Take me to " + "<a href='" + d.link + "' >"  + d.hero + " web page ⇢"+ "</a>" ); 
           })

          .on( 'mouseenter', function() {
            // select element in current context
            d3.select( this )
              .transition()
              .attr("x", function(d) { return -60;})
              .attr("y", function(d) { return -60;})
              .attr("height", 100)
              .attr("width", 100);
          })
          // set back
          .on( 'mouseleave', function() {
            d3.select( this )
              .transition()
              .attr("x", function(d) { return -25;})
              .attr("y", function(d) { return -25;})
              .attr("height", 50)
              .attr("width", 50);
          });

  // Append hero name on roll over next to the node as well
  nodeEnter.append("text")
      .attr("class", "nodetext")
      .attr("x", x_browser)
      .attr("y", y_browser +15)
      .attr("fill", tcBlack)
      .text(function(d) { return d.hero; });


  // Exit any old nodes.
  node.exit().remove();


  // Re-select for update.
  path = vis.selectAll("path.link");
  node = vis.selectAll("g.node");

function tick() {


    path.attr("d", function(d) {

     var dx = d.target.x - d.source.x,
           dy = d.target.y - d.source.y,
           dr = Math.sqrt(dx * dx + dy * dy);
           return   "M" + d.source.x + "," 
            + d.source.y 
            + "A" + dr + "," 
            + dr + " 0 0,1 " 
            + d.target.x + "," 
            + d.target.y;
  });
    node.attr("transform", nodeTransform);    
  }
}


/**
 * Gives the coordinates of the border for keeping the nodes inside a frame
 * http://bl.ocks.org/mbostock/1129492
 */ 
function nodeTransform(d) {
  d.x =  Math.max(maxNodeSize, Math.min(w - (d.imgwidth/2 || 16), d.x));
    d.y =  Math.max(maxNodeSize, Math.min(h - (d.imgheight/2 || 16), d.y));
    return "translate(" + d.x + "," + d.y + ")";
   }

/**
 * Toggle children on click.
 */ 
function click(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }

  update();
}


/**
 * Returns a list of all nodes under the root.
 */ 
function flatten(root) {
  var nodes = []; 
  var i = 0;

  function recurse(node) {
    if (node.children) 
      node.children.forEach(recurse);
    if (!node.id) 
      node.id = ++i;
    nodes.push(node);
  }

  recurse(root);
  return nodes;
} 


</script>

</body>
</html>

marvel.json

{
 "name": "marvel",
 "img": "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/marvel.png",
 "children": [
  {
   "name": "Heroes",
   "children": [
    {
      "hero": "Spider-Man",
      "name": "Peter Benjamin Parker", 
      "link": "http://marvel.com/characters/54/spider-man",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/top_spiderman.png",
      "size": 40000,
      "popularity": 1
    },
    {
      "hero": "CAPTAIN MARVEL",
      "name": "Carol Danvers", 
      "link": "http://marvel.com/characters/9/captain_marvel",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/top_captainmarvel.png",
      "size": 40000,
      "popularity": 3
    },
    {
      "hero": "HULK", 
      "name": "Robert Bruce Banner",
      "link": "http://marvel.com/characters/25/hulk",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/top_hulk.png",
      "size": 40000,
      "popularity": 6
    },
    {
      "hero": "Black Widow", 
      "name": "Natalia 'Natasha' Alianovna Romanova",
      "link": "http://marvel.com/characters/6/black_widow",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/top_blackwidow.png",
      "size": 40000,
      "popularity": 7
    },
    {
      "hero": "Daredevil", 
      "name": "Matthew Michael Murdock",
      "link": "http://marvel.com/characters/11/daredevil",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/top_daredevil.png",
      "size": 40000,
      "popularity": 7
    },
    {
      "hero": "Wolverine", 
      "name": "James Howlett",
      "link": "http://marvel.com/characters/66/wolverine",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/top_wolverine.png",
      "size": 40000,
      "popularity": 8
    },
    {
      "hero": "Captain America", 
      "name": "Steven Rogers",
      "link": "http://marvel.com/characters/8/captain_america",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/top_captainamerica.png",
      "size": 40000,
      "popularity": 9
    },
    {
      "hero": "Iron Man", 
      "name": "Anthony Edward 'Tony' Stark",
      "link": "http://marvel.com/characters/29/iron_man",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/top_ironman.png",
      "size": 40000,
      "popularity": 4
    },
    {
      "hero": "THOR", 
      "name": "Thor Odinson",
      "link": "http://marvel.com/characters/60/thor",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/top_thor.png",
      "size": 40000,
      "popularity": 5
    }
  ]
  },
  {
   "name": "Villains",
   "children": [
    {
      "hero": "Dr. Doom",
      "name": "Victor von Doom", 
      "link": "http://marvel.com/characters/13/dr_doom",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/drdoom.png",
      "size": 40000,
      "popularity": 6
    },
    {
      "hero": "Mystique",
      "name": "Unrevealed", 
      "link": "http://marvel.com/characters/1552/mystique",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/mystique.png",
      "size": 40000,
      "popularity": 8
    },
    {
      "hero": "Red Skull",
      "name": "Johann Shmidt", 
      "link": "http://marvel.com/characters/1901/red_skull",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/redskull.png",
      "size": 40000,
      "popularity": 9
    },
    {
      "hero": "Ronan",
      "name": "Ronan", 
      "link": "http://marvel.com/characters/49/ronan",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/ronan.png",
      "size": 40000,
      "popularity": 8
    },
    {
      "hero": "Magneto",
      "name": "Max Eisenhardt", 
      "link": "http://marvel.com/characters/35/magneto",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/magneto.png",
      "size": 40000,
      "popularity": 8
    },
    {
      "hero": "Thanos",
      "name": "Thanos", 
      "link": "http://marvel.com/characters/58/thanos",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/thanos.png",
      "size": 40000,
      "popularity": 7
    },
    {
      "hero": "Black Cat",
      "name": "Felicia Hardy", 
      "link": "http://marvel.com/characters/271/black_cat",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/blackcat.png",
      "size": 40000,
      "popularity": 6
    }
   ]
  },
  {
   "name": "Teams",
   "children": [
    {
      "hero": "Avengers",
      "name": "", 
      "link": "http://marvel.com/characters/68/avengers",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/avengers.png",
      "size": 40000,
      "popularity": 6
    },
    {
      "hero": "Guardians of the Galaxy",
      "name": "", 
      "link": "http://marvel.com/characters/70/guardians_of_the_galaxy",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/gofgalaxy.png",
      "size": 40000,
      "popularity": 5
    },
    {
      "hero": "Defenders",
      "name": "", 
      "link": "http://marvel.com/characters/534/defenders",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/defenders.png",
      "size": 40000,
      "popularity": 2
    },
    {
      "hero": "X-Men",
      "name": "", 
      "link": "http://marvel.com/characters/71/x-men",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/xmen.png",
      "size": 40000,
      "popularity": 3
    },
    {
      "hero": "Fantastic Four",
      "name": "", 
      "link": "http://marvel.com/characters/69/fantastic_four",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/fantasticfour.png",
      "size": 40000,
      "popularity": 3
    },
    {
      "hero": "Inhumans",
      "name": "", 
      "link": "http://marvel.com/characters/1040/inhumans",
      "img":  "https://dl.dropboxusercontent.com/u/19954023/marvel_force_chart_img/inhumans.png",
      "size": 40000,
      "popularity": 9
    }
   ]
  }  

您可以使用 d3.js 的 .filter() 功能。 将扁平化的 json 记录存储在全局变量中。为 'all' 和 'popular' 按钮设置一个 onclick 侦听器。 'popular' 按钮的 onclick 侦听器可以类似于:

function filterPopular(){
  tempNodes = nodes.filter(function(d){
    return !('popularity' in d) || d.popularity>5;
  });
  update(tempNodes);
}

.filter() 本质上做的是为回调函数保留 return 为真的记录。在这种情况下,没有流行度字段(非叶节点)或流行度大于 5 的记录将保留在 tempNodes 中。我将过滤后的记录存储到一个新的 var 中,以便在您单击 'all' 按钮时全局维护记录。使用 tempNodes 调用更新。节点现在将消失,但从父节点到已删除子节点的链接将保留。您必须想办法删除这些链接。 希望这至少能有所帮助!