简体   繁体   中英

SVG array not available to HTML page

I'm referencing a .js file from my .html file and trying to append my SVG charts into specific div's. However, I get the following message in the console when I open the html file:

TypeError: svg[0] is undefined

When I include the script tag in my html along with my script.js code, I can find the "svg" array (and expected pie chart's) from the console. [I used that data to build the forEach loop at the end of stack.js below.] I know that the stack.js code works because it's the same code from this question which I posted a few days ago.

I'm curious as to why I cannot get my .html file to "see" the array from my .js file. Is there a way to do this, or am I missing a best-practice with d3 which I should be using? Is it somehow related to d3 being asynchronous ?

stack.html:

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script src="stack.js"></script>
</head>

<body>
    <div id="pie1"></div>
    <div id="pie2"></div>
</body>
</html>

stack.js

var data  = {
    "data":[
        {"pie_one": {
            "id": "pie1",
            "results": [
                {"ratio": 0.04, "total": 7, "failures": 1},
                {"ratio": 0.04, "total": 8, "failures": 0},
                {"ratio": 0.04, "total": 9001, "failures": 0}
            ]}
        },
        {"pie_two": {
            "id": "pie2",
            "results": [
                {"ratio": 0.04, "total": 10, "failures": 0},
                {"ratio": 0.04, "total": 11, "failures": 1},
                {"ratio": 0.04, "total": 12, "failures": 1}
            ]}
        }
    ]
};

var width = 300,
    height = 300,
    radius = Math.min(width, height) / 2;

var arc = d3.svg.arc()
    .outerRadius(radius)
    .innerRadius(radius - (radius / 2));

var pie = d3.layout.pie()
    .sort(null)
    .value(function(d) { return d.ratio; });

var svg = d3.select("body").selectAll("svg")
   .data(data.data)
   .enter()
   .append("svg")
   .attr("width", width)
   .attr("height", height)
   .append("g")
   .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

svg[0].forEach(function(pie) {
        var div = document.getElementById(pie.__data__.id);
        div.innerHTML = pie;
    });

var g = svg.selectAll(".arc")
    .data(function(d,i) { return pie(d["pie"+(i+1)].results) })
    .enter().append("g")
    .attr("class", "arc")

    g.append("path")
     .attr("d", arc)
     .style("fill", function(d) { return (d.data.failures > 0) ? "#d63535" : "#22c12d"; });

window.onload = function() {
    svg[0].forEach(function(pie) {
        var div = document.getElementById(pie.__data__.id);
        div.innerHTML = pie;
    });
};

The reason that you can't reference your svg[0] is-- as I mentioned in my comment-- because you are trying to do things before the DOM is loaded. Moving your code into the window.onload would fix that particular error but you have additional problems with the way you're trying to do things.

Below is a working example and is more what you want to do. You should be using d3's functionality to embed your charts where you want them. Rather than sticking them in the body and then trying to move them around.

stack.js

var data  = {
    "data":[
        {
            "id": "pie1",
            "results": [
                {"ratio": 0.04, "total": 7, "failures": 1},
                {"ratio": 0.04, "total": 8, "failures": 0},
                {"ratio": 0.04, "total": 9001, "failures": 0}
            ]
        },
        {
            "id": "pie2",
            "results": [
                {"ratio": 0.04, "total": 10, "failures": 0},
                {"ratio": 0.04, "total": 11, "failures": 1},
                {"ratio": 0.04, "total": 12, "failures": 1}
            ]
        }
    ]
};

window.onload = function() {

var width = 300,
    height = 300,
    radius = Math.min(width, height) / 2;

var arc = d3.svg.arc()
    .outerRadius(radius)
    .innerRadius(radius - (radius / 2));

var pie = d3.layout.pie()
    .sort(null)
    .value(function(d) { return d.ratio; });

var svg = d3.select("body")
   .selectAll("div")
   .data(data.data, function(d) { return d ? d.id : this.id })
   .append("svg")
   .attr("width", width)
   .attr("height", height)
   .append("g")
   .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

var g = svg.selectAll(".arc")
    .data(function(d) { return pie(d.results) })
    .enter().append("g")
    .attr("class", "arc")
    .append("path")
    .attr("d", arc)
    .style("fill", function(d) { return (d.data.failures > 0) ? "#d63535" : "#22c12d"; });

};

The key to getting the charts where you want them is in the second parameter of the data binding call.

   .selectAll("div")
   .data(data.data, function(d) { return d ? d.id : this.id })

The above generates a key value for both the elements and your data rows-- where those keys match, d3 binds that particular data row to the data element.

I also changed up your data structure a tiny bit because the way you're doing things with unique keys to identify your objects makes it difficult to work with d3's idea of data. If you can't change up your data structure for some reason, then I'd suggest an intermediate function to reshape your data into something easier to work with. The other poster in your previous question commented on your data structure as well.

Your stack.html is unchanged.

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script src="stack.js"></script>
</head>

<body>
    <div id="pie1"></div>
    <div id="pie2"></div>
</body>
</html>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM