我遇到了 link.exit()。remove(); 和 node.exit()。remove(); 的问题。如果我在initializeGraph方法中设置它,那么我认为使用tick函数会出现几个错误。所以我的问题是如何或为什么我会得到这些错误:
Uncaught TypeError: undefined is not a function graph-d3.js:156initializeGraph graph-d3.js:156updateForceUsingNewNodes graph-d3.js:108createGraph graph-d3.js:18$.ajax.success ajax-stuff.js:106j jquery-2.1.1.min.js:2k.fireWith jquery-2.1.1.min.js:2x jquery-2.1.1.min.js:4n.prop.on.c jquery-2.1.1.min.js:4n.event.dispatch jquery-2.1.1.min.js:3r.handle jquery-2.1.1.min.js:3
3Error: Invalid value for <line> attribute x1="NaN" d3.min.js:1
3Error: Invalid value for <line> attribute y1="NaN" d3.min.js:1
3Error: Invalid value for <line> attribute x2="NaN" d3.min.js:1
3Error: Invalid value for <line> attribute y2="NaN" d3.min.js:1
Uncaught TypeError: Cannot read property 'attr' of undefined
这是源代码的一个例外。不重要的行被删除:
var alreadyThere = false;
var nodeCircles = {};
var svg, link, node;
var force = d3.layout.force();
var width = 700, height = 200;
var boxIDName = "#main-rightinfo";
var currentJSON;
var container;
var zoom = d3.behavior.zoom()
.scaleExtent([0.4, 5]);
var drag = force.drag();
function createGraph(newJSON){
if (alreadyThere){
svg.remove();
nodeCircles = {};
}
updateForceUsingNewNodes(generateObjects(newJSON));
alreadyThere = true;
currentJSON = newJSON;
force.start();
}
function updateGraph(newJSON){
svg.remove();
findDuplicatesAndSetEmpty(newJSON);
deleteEmptyObjectsInJSON(newJSON);
currentJSON = currentJSON.concat(newJSON);
updateForceUsingNewNodes(generateObjects(currentJSON));
force.start();
}
//here are some methods forming the json and array...
function initializeGraph(){
zoom.on("zoom", zoomed);
drag.on("dragstart", dragstart);
force
.size([width, height])
.gravity(.1)
.charge(-400)
.friction(0.9)
.theta(0.9)
.linkStrength(0.9)
.distance(55)
.alpha(0.1)
.on("tick", tick);
svg = d3.select("#main-right")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom).on("dblclick.zoom", null);
svg
.append("svg:defs").selectAll("marker")
.data(["end"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 32)
.attr("refY", -0.05)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.attr('fill', '#359AF4');
container = svg.append("g");
link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(force.links())
.enter().append("line")
.attr("class", "link")
.attr("marker-end", "url(#end)");
node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", function(d) { click(d); })
.on("dblclick", function(d) { dblclick(d); })
.on('contextmenu', function(data, index) {
d3.event.preventDefault();
updateGraphByRemoveElement(data, index);
})
.call(drag);
node
.append("circle")
.attr("r", 20)
.attr("cx", 0)
.attr("cy", 0)
.style("fill", '#eee')
.style("stroke", '#fff')
.style("stroke-width", '0.5px');
node
.append("image")
.attr("xlink:href", function(d) {
if (d.class == "Person") {
return "pics/node_person.svg";
} else {
return "pics/node_appln.svg";
}} )
.attr("x", -20)
.attr("y", -20)
.attr("width", 40)
.attr("height", 40)
.attr("color", "white");
node
.append("text")
.attr("x", 20)
.attr("y", 4)
.style("fill", "#bbb")
.text(function(d) { return d.name; });
node
.append("circle")
.attr("r", 7)
.attr("cx", 0)
.attr("cy", -16)
.style("fill", '#359AF4');
node
.append("text")
.attr("text-anchor", "center")
.attr("x", -3)
.attr("y", -13)
.style("fill", "white")
.text(function(d) { return d.linkCount; });
function tick() {
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; });
node
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
}
//here are some functions tick, mousedown and so on...
您可以从代码中看到 svg.remove(); 。但是直到exit()。remove()不起作用,它才需要。所以是的,如何处理tickevent / .exit()。remove()。
感谢您的任何提示。
PS:我使用了这个非常基本的https://gist.github.com/mbostock/1095795 和另一个非常接近我的D3.js - exit() section does not remove all data 这也不起作用Why does d3.js v3 break my force graph when implementing zooming when v2 doesn't?
var alreadyThere = false;
var nodeCircles = {};
var svg, link, node;
var force = d3.layout.force();
var width = 700, height = 400;
var boxIDName = "#main-rightinfo";
var currentJSON;
var container;
var zoom = d3.behavior.zoom()
.scaleExtent([0.4, 5]);
var drag = force.drag();
function createGraph(newJSON){
if (alreadyThere){
//svg.remove();
nodeCircles = {};
}
updateForceUsingNewNodes(generateObjects(newJSON));
alreadyThere = true;
currentJSON = newJSON;
force.start();
}
function updateGraph(newJSON){
//svg.remove();
findDuplicatesAndSetEmpty(newJSON);
deleteEmptyObjectsInJSON(newJSON);
currentJSON = currentJSON.concat(newJSON);
updateForceUsingNewNodes(generateObjects(currentJSON));
force.start();
}
function findDuplicatesAndSetEmpty(newJSON){
for (var i = 0; i < currentJSON.length; i++) {
for (var o = 0; o < newJSON.length; o++) {
if ((currentJSON[i].source.ID == newJSON[o].source) && (currentJSON[i].target.ID == newJSON[o].target)
|| (currentJSON[i].source.ID == newJSON[o].target) && (currentJSON[i].target.ID == newJSON[o].source)){
newJSON[o] = {};
}
}
}
}
function deleteEmptyObjectsInJSON(json){
for (var i = 0; i < json.length; i++) {
var y = json[i].source;
if (y==="null" || y===null || y==="" || typeof y === "undefined"){
json.splice(i,1);
i--;
}
}
}
function updateGraphByRemoveElement(clickedNode, index){
svg.remove();
var json4Splicing = currentJSON;
for (var i = 0; i < json4Splicing.length; i++) {
if (json4Splicing[i].source.ID == clickedNode.ID){
json4Splicing[i] = {};
} else if (json4Splicing[i].target.ID == clickedNode.ID){
json4Splicing[i] = {};
}
}
deleteEmptyObjectsInJSON(json4Splicing);
deleteNode(force.nodes(),clickedNode);
currentJSON = json4Splicing;
updateForceRemoveElement(generateObjects(currentJSON));
}
function deleteNode(allNodes, clickedNode){
allNodes.forEach(function(node) {
if (node == clickedNode){
force.links().forEach(function(link) {
if (node.ID == link.source.ID){
link.target.linkCount--;
}
if (node.ID == link.target.ID){
link.source.linkCount--;
}
});
node.linkCount = 0;
}
});
}
function generateObjects(json) {
json.forEach(function(link) {
if (typeof(link.source) == "string"){
link.source = nodeCircles[link.source] || (nodeCircles[link.source] = {name: link.sourceName, ID: link.source, class: link.sourceClass, linkCount:0});
link.source.linkCount++;
}
if (typeof(link.target) == "string"){
link.target = nodeCircles[link.target] || (nodeCircles[link.target] = {name: link.targetName, ID: link.target, class: link.targetClass, linkCount:0});
link.target.linkCount++;
}
});
return json;
}
function updateForceRemoveElement(links){
force.nodes(d3.values(nodeCircles).filter(function(d){ return d.linkCount; }) );
force.links(d3.values(links));
initializeGraph();
}
function updateForceUsingNewNodes(links){
force.nodes(d3.values(nodeCircles).filter(function(d){ return d.linkCount; }) );
force.links(d3.values(links));
initializeGraph();
}
function initializeGraph(){
zoom.on("zoom", zoomed);
drag.on("dragstart", dragstart);
force
.size([width, height])
.gravity(.1)
.charge(-400)
.friction(0.9)
.theta(0.9)
.linkStrength(0.9)
.distance(55)
.alpha(0.1)
.on("tick", tick);
svg = d3.select("#main-right")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom).on("dblclick.zoom", null);
svg
.append("svg:defs").selectAll("marker")
.data(["end"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 32)
.attr("refY", -0.05)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.attr('fill', '#359AF4');
container = svg.append("g");
link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(force.links())
.enter().append("line")
.attr("class", "link")
.attr("marker-end", "url(#end)");
link.exit().remove();
node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", function(d) { click(d); })
.on("dblclick", function(d) { dblclick(d); })
.on('contextmenu', function(data, index) {
d3.event.preventDefault();
updateGraphByRemoveElement(data, index);
})
.call(drag);
node
.append("circle")
.attr("r", 20)
.attr("cx", 0)
.attr("cy", 0)
.style("fill", '#eee')
.style("stroke", '#fff')
.style("stroke-width", '0.5px');
node
.append("image")
.attr("xlink:href", function(d) {
if (d.class == "Person") {
return "pics/node_person.svg";
} else {
return "pics/node_appln.svg";
}} )
.attr("x", -20)
.attr("y", -20)
.attr("width", 40)
.attr("height", 40)
.attr("color", "white");
node
.append("text")
.attr("x", 20)
.attr("y", 4)
.style("fill", "#bbb")
.text(function(d) { return d.name; });
node
.append("circle")
.attr("r", 7)
.attr("cx", 0)
.attr("cy", -16)
.style("fill", '#359AF4');
node
.append("text")
.attr("text-anchor", "center")
.attr("x", -3)
.attr("y", -13)
.style("fill", "white")
.text(function(d) { return d.linkCount; });
node.exit().remove();
}
function tick() {
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; });
node
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
function zoomed() {
d3.event.sourceEvent.stopPropagation();
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstart(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("fixed", d.fixed = true);
}
function mouseover() {
d3.select(this).select("text").transition()
.duration(750)
.style("font-size","15px")
.style("fill","black");
d3.select(this).moveToFront();
}
function mouseout() {
d3.select(this).select("text").transition()
.duration(750)
.style("font-size","10px")
.style("fill","#ccc");
}
function click(d) {
$(boxIDName).empty();
if (d.class == "Person"){
$(boxIDName).append("<h2>Person/Company</h2>");
getNodeInfoPerson(d.ID);
}
if (d.class == "Appln"){
$(boxIDName).append("<h2>Application</h2>");
getNodeInfoAppln(d.ID);
}
}
function dblclick(d) {
entryClicked(d.ID+"|"+d.class,false);
}