D3未捕获的TypeError:无法读取未定义的属性“ age”

时间:2018-07-15 19:50:50

标签: javascript arrays json d3.js radio-button

我目前正在使用D3创建力导向图。 我一直在尝试使用HTML页面上的单选按钮创建年龄过滤器(使用年龄范围)。另外,我也在使用导入的JSON文件。

场景:我试图达到的预期结果是,当用户选择一个单选按钮时,应该看到具有特定年龄(从JSON文件创建)并且在范围内且符合条件的节点,而其他节点散焦。

到目前为止,每当我单击每个单选按钮时,都会连续出现一条错误消息,指出“未捕获的TypeError:无法读取未定义的属性'age'”。我确实查看了我的代码,看看它有什么问题。但是,我仍然不明白。在运行控制台时,错误出现在代码的此特定行“ if(d.age> = ageBracket [0] && d.age <= ageBracket [1])”。我的猜测是我是否必须为“ d.age”运行另一个循环?

**编辑:我添加了更多的JavaScript代码和HTML代码。

下面包含我的代码。

1。)Javascript代码:

(function(){
  function sayHello(){
    var name = "Hi John";
    return
    {
      fullName: name
    }
  } 

  console.log(sayHello().fullName);
})();

2。)HTML代码

function createGraph() {

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var radius = 25;

/////////////////////////////////////////////////////////////////////////

d3.json("patient.json", function(error, d) {

    if (error) throw error;

    var dataset_nodes = d.nodes;
    var dataset_links = d.links;

    // console.log(dataset_nodes);
    //console.log(dataset_links);

    var simulation = d3.forceSimulation()
        .nodes(dataset_nodes);

    var link_force = d3.forceLink(dataset_links).id(function(d) { return d.id; })
        .distance(function(d) { return d.value; })
        .strength(1);

    var charge_force = d3.forceManyBody()
        .strength(-150); //initial value is 100

    var center_force = d3.forceCenter(width / 2, height / 2);

    simulation
        .force("charge_force", charge_force)
        .force("center_force", center_force)
        .force("links", link_force)



    //add tick instructions: 
    simulation.on("tick", tickActions);

    //add encompassing group for the zoom 
    var g = svg.append("g")
        .attr("class", "everything");

    //draw lines for the links 
    var link = g.append("g")
        .attr("class", "links")
        .selectAll("line")
        .data(dataset_links)
        .enter().append("line")
        .attr("stroke-width", 2)
        .style("stroke", "#000000");

    //draw circles for the nodes 
    var node = g.append("g")
        .attr("class", "nodes")
        .selectAll("circle")
        .data(dataset_nodes)
        .enter()
        .append("circle")
        .attr("r", radius)
        .attr("fill", "#000000");



    //add drag capabilities  
    var drag_handler = d3.drag()
        .on("start", drag_start)
        .on("drag", drag_drag)
        .on("end", drag_end);

    drag_handler(node);


    //add zoom capabilities 
    var zoom_handler = d3.zoom()
        .on("zoom", zoom_actions);

    zoom_handler(svg);



    function drag_start(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
    }

    //make sure you can't drag the circle outside the box
    function drag_drag(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }

    function drag_end(d) {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
    }

    //Zoom functions 
    function zoom_actions() {
        g.attr("transform", d3.event.transform)
    }

    function tickActions() {
        //update circle positions each tick of the simulation 
        node.attr("cx", function(d) { return d.x; })
            .attr("cy", function(d) { return d.y; });

        //update link positions 
        link.attr("x1", function(d) { return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });

    }



    //Code for radio button filter on age range
    d3.selectAll("input[name=radiob]").on("change", function(d) {



        var age = document.getElementsByName("radiob");
        var ageBracket;


        node.style("opacity", 1);
        link.style("opacity", 1);


        for(var i=0; i < age.length; i++) {

            if(age[i].checked) {

                ageBracket = age[i].value.split("-"); 


                        if(d.age >= ageBracket[0] && d.age <= ageBracket[1])
                    {
                           node.filter(function(d) {
                           return d.age != ageBracket;
                         })
                           .style("opacity", "0.2");

                           link.filter(function(d) {
                           return d.source.age != ageBracket &&
                                  d.target.age != ageBracket;
                  })
                           .style("opacity", "0.2");

                           link.filter(function(d) {
                           return d.source.age == ageBracket ||
                                  d.target.age == ageBracket;
                  })
                            .style("opacity", "1"); 
                    }



            }
        }





    });




}); //End of reading json file


}

3。)JSON文件的一小部分

   <!doctype html>
   <html lang="en">
   <head>
   <meta charset="utf-8">
   <title>D3 Visualization-Force Directed Graph</title>
   <link href="style2.css" rel="stylesheet">
   <script src="https://d3js.org/d3.v4.min.js"></script>
   <script src="scripts2.js"></script>
   <link href="https://fonts.googleapis.com/css?family=Ubuntu" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css?family=Oswald"  rel="stylesheet">
    </head>

    <body>
    <svg width="950" height="820"></svg>


    <script>
      createGraph();
    </script>


    <h3 id="title3">Age</h3>
    <div class="radioButtonBoxMenu">
    <label><input name="radiob" type="radio" value="0-20">0-20</label><br>
    <label><input name="radiob" type="radio" value="20-30">20-30</label><br>
    <label><input name="radiob" type="radio" value="30-40">30-40</label><br>
    <label><input name="radiob" type="radio" value="40">40+</label><br>
    </div>

2 个答案:

答案 0 :(得分:1)

好的,所以您的代码无法正常工作的原因是因为您将包含数据的数组视为实际节点。您需要选择在“ createGraph”函数中创建的实际svg“节点”和“链接”。这是一个codepen:https://codepen.io/anon/pen/wxMmvm?editors=0011

要解决此问题,我选择了“节点”和“链接”,然后使用'.each'在这些对象中的每个对象上调用一个函数,以检查数据是否包含所选范围内的年龄值:

//Code for radio button filter on age range
d3.selectAll("input[name=radiob]").on("change", function(d) {
  var self = this;
  var ageBracket = {
    min : self.value.split('-')[0],
    max : self.value.split('-')[1]
  };

  d3.selectAll('.nodes')
    .selectAll('circle')
    .each(function (d) { 
      if (d.age >= ageBracket.min && d.age <= ageBracket.max) {
        d3.select(this).style('opacity', 1)
      } else {
        d3.select(this).style('opacity', 0.2)
      }
    })

  d3.selectAll('.links')
    .selectAll('line')
    .each(function (d) { 
      if (d.target.age >= ageBracket.min && d.target.age <= ageBracket.max) {
        d3.select(this).style('opacity', 1)
      } else {
        d3.select(this).style('opacity', 0.2)
      }
    })
});

注意:

  • 我不完全确定函数中处理哪个循环 单选按钮更改事件正在执行,因此我将其删除。
  • 您的答案中的JSON数据无效,因此我只使用了 此bl.ock:https://bl.ocks.org/mbostock/4062045。然后我 用与年龄/性别有关的数据填充它,因此您 必须将其换回去。
  • 我更改了单选按钮中的值,并添加了另一个“任意”以适合我的代码以获取“ ageBracket”,请检查上面链接的代码笔中的HTML。

答案 1 :(得分:1)

我将几家医院添加到json文件中

{
"nodes":[

    { "id": "1", "name": "John", "age": "31", "gender": "M"},
    { "id": "2", "name": "Emily", "age": "23", "gender": "F" },
    { "id": "3", "name": "Crystal", "age": "23", "gender": "F" },
    { "id": "4", "name": "Himiko", "age": "23", "gender": "F" },

    { "id": "hospital1", "name": "H1", "age": "31", "gender": "M"},
    { "id": "hospital2", "name": "H2", "age": "23", "gender": "F" },
    { "id": "hospital3", "name": "H3", "age": "23", "gender": "F" },
    { "id": "hospital4", "name": "H4", "age": "23", "gender": "F" }
],

   "links": [
    { "source": "hospital1", "target": "1", "value": 200 },
    { "source": "hospital2", "target": "2", "value": 200 },
    { "source": "hospital3", "target": "3", "value": 200 },
    { "source": "hospital4", "target": "4", "value": 200 }]
}

它能够绘制节点和链接。

然后选择单选按钮显示了对d的访问字段的违反。单选按钮更改回调没有参数。 要比较年龄,您需要构造一个年龄比较函数,并使用它来更新节点和链接的不透明度。

    d3.selectAll("input[name=radiob]").on("change", function () {
        var age = document.getElementsByName("radiob");
        var ageBracket;
        node.style("opacity", 1);
        link.style("opacity", 1);
        for(var i=0; i < age.length; i++) {
            if(age[i].checked) {
                ageBracket = age[i].value.split("-");
                var testAgeBracket = function (d) { return (d >= ageBracket[0] && d <= ageBracket[1]) };
                node.filter(function(d) { return testAgeBracket(d.age); })
                .style("opacity", "0.2");

                link.filter(function(d) {
                    return !testAgeBracket(d.source.age) &&
                           !testAgeBracket(d.target.age); })
                .style("opacity", "0.2");

                link.filter(function(d) {
                    return testAgeBracket(d.source.age) ||
                           testAgeBracket(d.target.age); })
                .style("opacity", "1"); 
            }
        }
    });