简体   繁体   中英

javascript/D3 basics: If asynchronous call, why is code outside of json request?

After reading variable scope in d3 javascript I assumed that in general you should place all your functions within the d3.json() {} request.

I am working with some map zooming code from http://techslides.com/demos/d3/us-zoom-county.html and am trying to figure out why the clicked(d) function is outside of the d3.json request.

Additionally, why won't the code work with the clicked(d) function inside the d3.json request?

var width = 960,
    height = 500,
    centered;

var projection = d3.geo.albersUsa()
    .scale(1070)
    .translate([width / 2, height / 2]);

var path = d3.geo.path()
    .projection(projection);

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

svg.append("rect")
    .attr("class", "background")
    .attr("width", width)
    .attr("height", height)
    .on("click", clicked);

var g = svg.append("g");

d3.json("data/us.json", function(error, us) {


 g.append("g")
      .attr("id", "counties")
    .selectAll("path")
      .data(topojson.feature(us, us.objects.counties).features)
    .enter().append("path")
  .attr("d", path)
  .attr("class", "county-boundary")
      .on("click", countyclicked);

  g.append("g")
      .attr("id", "states")
    .selectAll("path")
      .data(topojson.feature(us, us.objects.states).features)
    .enter().append("path")
  .attr("d", path)
  .attr("class", "state")
      .on("click", clicked);


  g.append("path")
      .datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
      .attr("id", "state-borders")
      .attr("d", path);
});


function clicked(d) {
  var x, y, k;

  if (d && centered !== d) {
    var centroid = path.centroid(d);
    x = centroid[0];
    y = centroid[1];
    k = 4;
    centered = d;
  } else {
    x = width / 2;
    y = height / 2;
    k = 1;
    centered = null;
  }

  g.selectAll("path")
      .classed("active", centered && function(d) { return d === centered; });

  g.transition()
      .duration(750)
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
      .style("stroke-width", 1.5 / k + "px");
}

function countyclicked(d) {
  alert(d.id);
}

I am trying to figure out why the clicked(d) function is outside of the d3.json request.

Where a function is defined is mostly irrelevant, it just needs to be accessible from where it should be referenced. clicked is accessible inside the d3.json callback, so that's fine.

The location of the function is only important if it is supposed to be a closure , ie needs access to values that are not passed as arguments. Eg if clicked needed to access us directly, it would have to be defined inside the callback.
That's not the case here though, clicked gets all the data it needs via its parameter.

However, what is important here is that the function is only being called after the data was received, since it is bound as event handler inside the callback ( .on("click", clicked) ) based on the data that was received.

why won't the code work with the clicked(d) function inside the d3.json request?

I don't see any reason why that would be the case.

That's because the function is also bound outside the callback to the container here:

svg.append("rect")
  .attr("class", "background")
  .attr("width", width)
  .attr("height", height)
  .on("click", clicked);

If you move inside the callback it is not accessible outside to that code anymore.

Note that clicked itself checks whether data is available or not by checking whether the argument d is set or not.

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