0

我通常对 d3 和网络编程都很陌生。我整理了一个基于 https://gist.github.com/mbostock/1153292的力布局图。该图在 Safari、Chrome 和 Opera 中运行良好(我还没有检查 IE)。但是当我尝试在 Firefox 中使用它时,我收到错误“Tick is not defined”。我使用的是 Firefox 12。

对此的任何建议将不胜感激谢谢,克莱尔

(代码为js脚本文件,鼠标点击触发,强制布局部分如下)。

d3.csv("data/sharing.csv?r1",  function(error, data) {
                    dataset = data
                    var nodes = {};

        dataset.forEach(function(link) {
        link.source = nodes[link.source] || (nodes[link.source] =    {name:link.source});
        link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
        });


        var w = 500;
        var h = 600;

                         var force = d3.layout.force()
            .nodes(d3.values(nodes))
            .links(dataset)
            .size([w-10,h-10]) 
            .linkDistance(60) 
            .charge(-375) 
            .on("tick", tick)
            .start();

        //Draw svg canvas
        var svg = d3.select("#svgContainer").append("svg").attr("id", "viz").attr("width", w).attr("height", h)

        // Create arrowheads
        svg.append("svg:defs").selectAll("marker")
            .data(["end-arrow"])
            .enter()
            .append("svg:marker")
            .attr("id", String)
            .attr("viewBox", "0 -5 10 10")
            .attr("refX", 15)
            .attr("refY", -1.5)
            .attr("markerWidth", 6)
            .attr("markerHeight", 6)
            .attr("orient", "auto")
            .attr("fill", "black") 
            .append("svg:path")
            .attr("d", "M0,-5L10,0L0,5");

        //Add links between the nodes and draw arrowhead at end of it.
        var path = svg.append("svg:g").selectAll("path")
            .data(force.links())
            .enter()
            .append("svg:path")
            .attr("stroke-width",2)
                        .attr("stroke", "black")
            .attr("fill","none")
            .attr("marker-end", "url(#end-arrow)");

        //Draw circles for nodes
        var circle = svg.append("svg:g").selectAll("circle")
            .data(force.nodes())
            .enter()
            .append("svg:circle")
            .attr("r", 6)
            .attr("fill", "white")
            .attr("stroke", "black")
            .call(force.drag)
            .on("mouseover", fade(.1))
            .on("mouseout", fade(1))

        //Label the nodes/circles
        var text = svg.append("svg:g").selectAll("g")
            .data(force.nodes())
            .enter()
            .append("svg:g")

        text.append("svg:text")
            .attr("x", 8)
            .attr("y", ".31em")
            .text(function(d) { return d.name; })

        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;
          });

          circle.attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
          });

          text.attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
          });
        }


=============REPLY TO COMMENT == FULL SCRIPT INCLUDING CALL TO CSV===

//If sharing button is clicked, load sharing data
d3.select("#sharing").on("click", function() {
d3.csv("data/sharing.csv?r1",  function(error, data) {
if (error)  
{//If error is not null,(i.e : something goes wrong), log the error.                            
window.console.log(error);      
} 
else 
{//If file loaded correctly, log the data to the console.   
dataset = data
window.console.log(dataset) 
color = getColor()
vizType = "force";

//Hide date fields/buttons as they are not applicable 
d3.select("#instructions").classed("hidden", true);
d3.select("#instructions2").classed("hidden", false);
d3.select("#startLabel").classed("hidden", true);   
d3.select("#startDate").classed("hidden", true);    
d3.select("#endLabel").classed("hidden", true); 
d3.select("#endDate").classed("hidden", true);  
d3.select("#removeFilter").classed("hidden", true); 
d3.select("#sharing").classed("hidden", true);
d3.select("#showData").classed("hidden", false);
d3.select("#showData").attr("value", "Back to Circles Vizualization");
d3.select("#tipsData").classed("hidden", true); 
d3.select("#ncpData").classed("hidden", true);      
d3.select("#tipsNCPData").classed("hidden", true);
d3.select("#tipsLabel").classed("hidden", true);    
d3.select("#ncpLabel").classed("hidden", true);     
d3.select("#tipsNCPLabel").classed("hidden", true);  

//Clear the previous viz and data
d3.select("#viz").remove();
d3.select("#stageTable").remove();
d3.select("#userTable").remove();

//Gets a count of sender records/source and stage/type          
var senderCount = getSortingCount(dataset,"Sender");
var stageCount = getSortingCount(dataset,"Stage");

//create tables summarising results
var summarySenderTable = tabulate(senderCount, ["Shared", "Sender"], vizType);  
var summaryStageTable = tabulate(stageCount, ["Shared", "Stage"], vizType);

var nodes = {};

// For each datapoint, check if a node exists already, if not create a new one. 
dataset.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] ={name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});

//Set the width and height for the svg, that will display the viz
var w = 500;
var h = 600;

var force = d3.layout.force()
          .nodes(d3.values(nodes))
          .links(dataset)
          .size([w-10,h-10])  
      .linkDistance(60) 
      .charge(-375) 
      .on("tick", tick)
      .start();

//Draw svg
var svg =    d3.select("#svgContainer").append("svg")
.attr("id","viz").attr("width",w).attr("height", h)

// Create arrowheads
svg.append("svg:defs").selectAll("marker")
          .data(["end-arrow"])
          .enter().append("svg:marker")
          .attr("id", String)
          .attr("viewBox", "0 -5 10 10")
          .attr("refX", 15)
          .attr("refY", -1.5)
          .attr("markerWidth", 6)
          .attr("markerHeight", 6)
          .attr("orient", "auto")
          .attr("fill", "black") 
          .append("svg:path")
          .attr("d", "M0,-5L10,0L0,5");

//Add links between the nodes and draw arrowhead at end of it.
var path = svg.append("svg:g").selectAll("path")
     .data(force.links())
     .enter()
     .append("svg:path")
     .attr("stroke-width",2)
     .attr("stroke", function(d){return color(d.ScreenName)})  
     .attr("fill","none")
     .attr("marker-end", "url(#end-arrow)");

//Draw circles for nodes
var circle = svg.append("svg:g").selectAll("circle")
       .data(force.nodes())
           .enter()
           .append("svg:circle")
           .attr("r", 6)
       .attr("fill", "white")
       .attr("stroke", "black")
       .call(force.drag)
       .on("mouseover", fade(.1))
       .on("mouseout", fade(1))

//Label nodes/circles
var text = svg.append("svg:g").selectAll("g")
          .data(force.nodes())
      .enter()
      .append("svg:g")  
       text.append("svg:text")
      .attr("x", 8)
      .attr("y", ".31em")
      .text(function(d) { return d.name; })  

//Set radius for arrows and applies transform 
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;
   });

   circle.attr("transform", function(d) {
   return "translate(" + d.x + "," + d.y + ")";
   });

   text.attr("transform", function(d) {
    return "translate(" + d.x + "," + d.y + ")";
   });
}   
//Allow for filter by row on stageTable
d3.select("#stage").select("#stageTable").selectAll("tr")
          .on("click", function(d){
          d3.select(this)
          var rowText = this.childNodes[1].innerHTML
          var svg = d3.select("#svgContainer").select("svg")
          var path = svg.selectAll("path")
                .style ("opacity", 1)
                .transition()
                    .duration(250)
                .style ("opacity", function(d){
          if(d.ScreenName == rowText){  
                      d3.selectAll("marker path").transition().style("stroke-opacity", 1);
          return fade(1)    
          }
         else{
        d3.selectAll("marker path").transition().style("stroke-opacity", 0.1);
          return 0.1
          })
        d3.select("#removeFilter").classed("hidden", false);                
          })

//Checks what links are connected to which(used for mouseover) 
var linkedByIndex = {};
dataset.forEach(function(d) {linkedByIndex[d.source.index + "," + d.target.index] = 1;});

function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] ||     a.index == b.index;
}
                                                        //Fades in/out circles and arrows on mouseover.
function fade(opacity) {
return function(d) {
circle.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
 });
path.style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
 });

};
}       

}   
})

})

Accessor for colour
function getColor(){
return color
}                           
4

1 回答 1

1

查看整个源代码有助于澄清事情。最顶部有一个 if/else 语句来检查错误。整个其余代码都在 else 块中。这就是造成问题的原因。

在条件块中定义函数声明(例如您的情况下的 tick() )具有特定于浏览器的怪异行为。 这是一篇很好的文章,它解释了函数声明函数表达式和定义不明确且支持不一致的函数语句之间的区别(这是您在 else 块中使用如此多的代码无意中创建的)。

如果您将代码从 else 块中提取出来,我认为跨浏览器的行为应该更可预测。

一般来说,创建巨大而长的条件块并不是一个好的编程习惯。它不仅引入了这些类型错误的可能性,而且很难阅读和理解。对于嵌套非常深的条件也是如此。

尽量保持你的条件相当严格,以便条件块内的代码直接对应于条件本身的含义。您应该能够阅读条件的意图并大声阻止内容,并且它们应该一起有意义。与条件无关的代码应尽可能位于包含它的函数的顶层。您可以通过将代码分解为有意义的函数并控制条件来提高可读性。

在上面的示例中,您可以执行以下操作:

if (error) {                            
    window.console.log(error);      
} 
else {
    window.console.log(dataset);
}

dataset = data 
color = getColor()
vizType = "force";
...
... rest of code

最后一条评论是JSLintJSHint之类的工具可以用来验证您的代码。它会自动指出这样的问题。有时它可能过于严格,但它是一个很好的学习体验,至少可以理解它在抱怨什么。

于 2013-08-09T02:15:52.760 回答