Related
I want to open a graph as a modal after clicking another graph. However, what shows up in the modal is plain text, like such:
After inspecting the element, it seems like the text is the <g> tag that was in the svg, but I'm not sure what that means.
^ The corresponding HTML code is <text fill="currentColor" x="-9" dy="0.32em">−1.5</text>
I'm not sure why this is happening and I can't find any similar problems online for this.
Here's my graph code:
function Wavelet() {
const gWidth = window.innerWidth / 5
const gWidth_modal = window.innerWidth/3
var margin = {top: 20, right: 30, bottom: 30, left: 20},
width = gWidth - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var margin_modal = {top: 20, right: 30, bottom: 30, left: 20},
width_modal = gWidth_modal - margin_modal.left - margin_modal.right,
height_modal = 400 - margin_modal.top - margin_modal.bottom;
// append the svg object to the body of the page
var svg = d3.select("#contour").selectAll("*").remove();
svg = d3.select("#contour").append("svg")
.attr("width", gWidth + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
var svg_modal = d3.select("#contour-modal").selectAll("*").remove();
svg_modal = d3.select("#contour-modal").append("svg_modal")
.attr("width", width_modal + margin_modal.left + margin_modal.right)
.attr("height", height_modal + margin_modal.top + margin_modal.bottom)
.append("g")
.attr("transform", "translate(" + margin_modal.left + "," + margin_modal.top + ")")
var color = d3.scaleLinear()
.domain([0, 0.01]) // Points per square pixel.
.range(["#c3d7e8", "#23527a"])
const data = [
{ x: 3.4, y: 4.2 },
{ x: -1, y: 4.2 },
{ x: -1, y: 2.8 },
{ x: 3.6, y: 4.3 },
{ x: -0.1, y: 3.7 },
{ x: 4.7, y: 2.5 },
{ x: 0.8, y: 3.6 },
{ x: 4.7, y: 3.7 },
{ x: -0.4, y: 4.2 },
{ x: 0.1, y: 2.2 },
{ x: 0.5, y: 3 },
{ x: 4.3, y: 4.5 },
{ x: 3.4, y: 2.7 },
{ x: 4.4, y: 3.6 },
{ x: 3.3, y: 0.6 },
{ x: 3, y: 3.4 },
{ x: 4.7, y: 0 },
{ x: -0.7, y: 2.7 },
{ x: 2.6, y: 2 },
{ x: 0, y: -1 },
{ x: 3.4, y: 4.5 },
{ x: 3.9, y: 4.6 },
{ x: 0.7, y: 3.9 },
{ x: 3, y: 0.2 }
];
// Add X axis
var x = d3.scaleLinear()
.domain([-2, 6])
.range([ 0, width ]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var x_modal = d3.scaleLinear()
.domain([-2, 6])
.range([ 0, width_modal ]);
svg_modal.append("g")
.attr("transform", "translate(0," + height_modal + ")")
.call(d3.axisBottom(x_modal));
// Add Y axis
var y = d3.scaleLinear()
.domain([-2, 5])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y));
var y_modal = d3.scaleLinear()
.domain([-2, 5])
.range([ height_modal, 0 ]);
svg_modal.append("g")
.call(d3.axisLeft(y_modal));
// compute the density data
var densityData = d3.contourDensity()
.x(function(d) { return x(d.x); }) // x and y = column name in .csv input data
.y(function(d) { return y(d.y); })
.size([width, height])
.bandwidth(20) // smaller = more precision in lines = more lines
(data)
// Add the contour: several "path"
svg
.selectAll("path")
.data(densityData)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", function(d) { return color(d.value); })
.attr("stroke", "#4285f4")
svg_modal
.selectAll("path")
.data(densityData)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", function(d) { return color(d.value); })
.attr("stroke", "#4285f4")
}
export default Wavelet
I have 2 svg as the one in the modal doesn't get rendered if I use the same one (for some reason). If that's what's causing me problems, please let me know how to fix it.
useEffect(() => {
(async () => {
await Wavelet()
})();
});
// const showModal = () => {setIsopen((prev) => !prev); console.log("aaaaaaaaaa");}
function showModal (id) {
setIsopen((prev) => !prev);
setModalID(id)
}
function Graph() {
const modal = <div>
<div id="contour" onClick={() => showModal(4)}>
</div>
{isOpen && (modalID == 4) && (
<ModalContent onClose={() => setIsopen(false)}>
<div className="modal_c">
<h3>{modalID}</h3>
<div id="contour-modal" >
</div>
<p> </p>
</div>
</ModalContent>
)}
</div>
return modal;
}
}
My modal class if anybody wants to reference it:
const modal = {
position: "fixed",
zIndex: 1,
left: 0,
top: 0,
width: "100vw",
height: "100vh",
overflow: "auto",
backgroundColor: "rgba(192,192,192,0.5)",
display: "flex",
alignItems: "center"
};
const close = {
position: "absolute",
top: "11vh",
right: "27vw",
color: "#000000",
fontSize: 40,
fontWeight: "bold",
cursor: "pointer"
};
const modalContent = {
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "50%",
height: "80%",
margin: "auto",
backgroundColor: "white"
// border: "2px solid #000000"
};
export const Modal = ({ onOpen, children }) => {
console.log(children)
return <div onClick={onOpen}> {children}</div>;
};
export const ModalContent = ({ onClose, children }) => {
return (
<div style={modal}>
<div style={modalContent}>
<span style={close} onClick={onClose}>
×
</span>
{children}</div>
</div>
);
};
Problem is with your this statement : d3.select("#contour-modal").append("svg_modal").In order to render the elements correctly, you need to append them to svg. d3.select("#contour-modal").append("svg") . svg_modal is an invalid tag for your requirement. Kindly read more about svg rendering. Done a basic test of your code:
const gWidth = window.innerWidth / 5
const gWidth_modal = window.innerWidth / 3
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 20
},
width = gWidth - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var margin_modal = {
top: 20,
right: 30,
bottom: 30,
left: 20
},
width_modal = gWidth_modal - margin_modal.left - margin_modal.right,
height_modal = 200 - margin_modal.top - margin_modal.bottom;
// append the svg object to the body of the page
var svg = d3.select("#contour").selectAll("*").remove();
svg = d3.select("#contour").append("svg")
.attr("width", gWidth + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
var svg_modal = d3.select("#contour-modal").selectAll("*").remove();
svg_modal = d3.select("#contour-modal").append("svg")
.attr("width", width_modal + margin_modal.left + margin_modal.right)
.attr("height", height_modal + margin_modal.top + margin_modal.bottom)
.append("g")
.attr("transform", "translate(" + margin_modal.left + "," + margin_modal.top + ")")
var color = d3.scaleLinear()
.domain([0, 0.01]) // Points per square pixel.
.range(["#c3d7e8", "#23527a"])
const data = [{
x: 3.4,
y: 4.2
},
{
x: -1,
y: 4.2
},
{
x: -1,
y: 2.8
},
{
x: 3.6,
y: 4.3
},
{
x: -0.1,
y: 3.7
},
{
x: 4.7,
y: 2.5
},
{
x: 0.8,
y: 3.6
},
{
x: 4.7,
y: 3.7
},
{
x: -0.4,
y: 4.2
},
{
x: 0.1,
y: 2.2
},
{
x: 0.5,
y: 3
},
{
x: 4.3,
y: 4.5
},
{
x: 3.4,
y: 2.7
},
{
x: 4.4,
y: 3.6
},
{
x: 3.3,
y: 0.6
},
{
x: 3,
y: 3.4
},
{
x: 4.7,
y: 0
},
{
x: -0.7,
y: 2.7
},
{
x: 2.6,
y: 2
},
{
x: 0,
y: -1
},
{
x: 3.4,
y: 4.5
},
{
x: 3.9,
y: 4.6
},
{
x: 0.7,
y: 3.9
},
{
x: 3,
y: 0.2
}
];
// Add X axis
var x = d3.scaleLinear()
.domain([-2, 6])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var x_modal = d3.scaleLinear()
.domain([-2, 6])
.range([0, width_modal]);
svg_modal.append("g")
.attr("transform", "translate(0," + height_modal + ")")
.call(d3.axisBottom(x_modal));
// Add Y axis
var y = d3.scaleLinear()
.domain([-2, 5])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
var y_modal = d3.scaleLinear()
.domain([-2, 5])
.range([height_modal, 0]);
svg_modal.append("g")
.call(d3.axisLeft(y_modal));
// compute the density data
var densityData = d3.contourDensity()
.x(function(d) {
return x(d.x);
}) // x and y = column name in .csv input data
.y(function(d) {
return y(d.y);
})
.size([width, height])
.bandwidth(20) // smaller = more precision in lines = more lines
(data)
// Add the contour: several "path"
svg
.selectAll("path")
.data(densityData)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", function(d) {
return color(d.value);
})
.attr("stroke", "#4285f4")
svg_modal
.selectAll("path")
.data(densityData)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", function(d) {
return color(d.value);
})
.attr("stroke", "#4285f4")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.5.0/d3.min.js" integrity="sha512-+rnC6CO1Ofm09H411e0Ux7O9kqwM5/FlEHul4OsPk4QIHIYAiM77uZnQyIqcyWaZ4ddHFCvZGwUVGwuo0DPOnQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="contour">
</div>
<div id="contour-modal">
</div>
Recently I have started learning D3.js v5 and I have to make different sorts of graph. One of those graphs is a stacked horizontal bar chart. I followed a tutorial in Udemy but I am not able to make this chart to work.
Can anybody help me out?
So, I know d3 uses a stack generator to generate stacked bar chart. I used it and checking the values in console it seems that the data is formatted pretty well and and can start generating stacked bar chart but i can't seem to understand why its not working.
<html>
<script src="https://d3js.org/d3.v5.min.js"></script>
<body></body>
<script type="text/javascript">
var Margin = {top: 30, right: 20, bottom: 40, left: 110};
var InnerWidth = 432 - Margin.left - Margin.right;
var InnerHeight = 432 - Margin.top - Margin.bottom;
var diff_variant=[{Missense_Mutation: 5, Silent: 4, Splice_Site: 0},
{Missense_Mutation: 8, Splice_Site: 0, Silent: 0},
{Splice_Site: 4, Missense_Mutation: 4, Silent: 0},
{Missense_Mutation: 4, Silent: 1, Splice_Site: 0},
{Missense_Mutation: 5, Splice_Site: 0, Silent: 0},
{Missense_Mutation: 5, Splice_Site: 0, Silent: 0},
{Missense_Mutation: 4, Splice_Site: 0, Silent: 0},
{Missense_Mutation: 4, Splice_Site: 0, Silent: 0},
{Silent: 1, Missense_Mutation: 1, Splice_Site: 0},
{Missense_Mutation: 1, Splice_Site: 0, Silent: 0}];
var data1=[{name: "Missense_Mutation", count: 41},
{name: "Silent", count: 6},
{name: "Splice_Site", count: 4}];
var variant_name=["Missense_Mutation", "Splice_Site", "Silent"];
var stack = d3.stack().keys(variant_name);
var x = d3.scaleLinear()
.domain([0, d3.max(data1, function(d){return d.count;})])
.range([0,InnerWidth]);
var x_axis = d3.scaleLinear()
.domain([0, d3.max(data1, function(d){return d.count;})])
.range([0,InnerWidth]);
var y_axis = d3.scaleLinear()
.domain([0, data1.length])
.range([0,InnerWidth]);
var y = d3.scaleBand()
.domain(data1.map(function(d){return d.name;}))
.rangeRound([0,InnerHeight])
.padding(0.1);
var svg5 = d3.select("body").append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", `0 0 ${InnerWidth + Margin.left + Margin.right} ${InnerHeight + Margin.top + Margin.bottom}`)
.style("background","lightblue")
.append("g")
.attr("transform", "translate(" + Margin.left + "," + Margin.top + ")");
svg5.append("g").style("font", "12px sans").call(d3.axisLeft(y));
svg5.append("g").attr('transform', `translate(0, ${InnerHeight})`).style("font", "12px times").call(d3.axisBottom(x_axis));
var series = svg5.selectAll("g").data(stack(diff_variant))
.enter().append("g")
.style("fill", (d,i)=>d3.schemeSet3[i]);
series.selectAll("rect").data(function(d){return d;})
.enter().append("rect")
.attr("height", 25)
.attr("width", function(d){return x(d[1]) - x(d[0]);})
.attr("x", function(d){return x(d[0]);})
.attr("y", function(d,i){return i*40;});
</script>
</html>
When you do this...
var series = svg5.selectAll("g")//etc...
... you are selecting <g> elements that already exist in the SVG (the two axes) and binding data to them. That's why your enter selection is empty.
You can select null (read more about it here):
var series = svg5.selectAll(null)
Or you can select by a class, that you later set in the enter selection:
var series = svg5.selectAll(".foo")
However, the best solution is just moving the axes' group to the bottom: that not only will fix your issue, but it will also paint the axis above the rectangles.
Here is your code with that change:
<html>
<script src="https://d3js.org/d3.v5.min.js"></script>
<body></body>
<script type="text/javascript">
var Margin = {
top: 30,
right: 20,
bottom: 40,
left: 110
};
var InnerWidth = 432 - Margin.left - Margin.right;
var InnerHeight = 432 - Margin.top - Margin.bottom;
var diff_variant = [{
Missense_Mutation: 5,
Silent: 4,
Splice_Site: 0
},
{
Missense_Mutation: 8,
Splice_Site: 0,
Silent: 0
},
{
Splice_Site: 4,
Missense_Mutation: 4,
Silent: 0
},
{
Missense_Mutation: 4,
Silent: 1,
Splice_Site: 0
},
{
Missense_Mutation: 5,
Splice_Site: 0,
Silent: 0
},
{
Missense_Mutation: 5,
Splice_Site: 0,
Silent: 0
},
{
Missense_Mutation: 4,
Splice_Site: 0,
Silent: 0
},
{
Missense_Mutation: 4,
Splice_Site: 0,
Silent: 0
},
{
Silent: 1,
Missense_Mutation: 1,
Splice_Site: 0
},
{
Missense_Mutation: 1,
Splice_Site: 0,
Silent: 0
}
];
var data1 = [{
name: "Missense_Mutation",
count: 41
},
{
name: "Silent",
count: 6
},
{
name: "Splice_Site",
count: 4
}
];
var variant_name = ["Missense_Mutation", "Splice_Site", "Silent"];
var stack = d3.stack().keys(variant_name);
var x = d3.scaleLinear()
.domain([0, d3.max(data1, function(d) {
return d.count;
})])
.range([0, InnerWidth]);
var x_axis = d3.scaleLinear()
.domain([0, d3.max(data1, function(d) {
return d.count;
})])
.range([0, InnerWidth]);
var y_axis = d3.scaleLinear()
.domain([0, data1.length])
.range([0, InnerWidth]);
var y = d3.scaleBand()
.domain(data1.map(function(d) {
return d.name;
}))
.rangeRound([0, InnerHeight])
.padding(0.1);
var svg5 = d3.select("body").append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", `0 0 ${InnerWidth + Margin.left + Margin.right} ${InnerHeight + Margin.top + Margin.bottom}`)
.style("background", "lightblue")
.append("g")
.attr("transform", "translate(" + Margin.left + "," + Margin.top + ")");
var series = svg5.selectAll("g").data(stack(diff_variant))
.enter().append("g")
.style("fill", (d, i) => d3.schemeSet3[i]);
series.selectAll("rect").data(function(d) {
return d;
})
.enter().append("rect")
.attr("height", 25)
.attr("width", function(d) {
return x(d[1]) - x(d[0]);
})
.attr("x", function(d) {
return x(d[0]);
})
.attr("y", function(d, i) {
return i * 40;
});
svg5.append("g").style("font", "12px sans").call(d3.axisLeft(y));
svg5.append("g").attr('transform', `translate(0, ${InnerHeight})`).style("font", "12px times").call(d3.axisBottom(x_axis));
</script>
</html>
I have data in the following format which I would like to plot using d3:
data = [
{ x: 0.2, y: [ 1, 2, 4 ] },
{ x: 0.3, y: [ 2 ] },
{ x: 0.5, y: [ 4, 7, 8, 12, 19 ] }
{ x: 1.4, y: [ 1, 3 ] }
]
Normally the y-axis values are integers, but here they are arrays, so the following code doesn't work as intended:
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d){ return x(d.x) })
.attr("cy", function(d){ return y(d.y) })
.attr("r", 2)
Instead of getting multiple circles plotted for each of the values in the array, I only get one.
Other similar questions on this website deal only with data that has a fixed number of y-axis values, so I haven't found a way to modify those solutions for this problem.
The traditional D3 answer here would be appending a group for each object and then appending a circle for each y value for each group.
However, since you seem to be a D3 beginner (correct me if I'm wrong), I'd suggest to just create a single array of objects, that you can pass to data.
There are several ways for doing this, such as:
const newData = data.reduce(function(a, c) {
return a.concat(c.y.map(function(d) {
return {
x: c.x,
y: d
}
}));
}, []);
Here is your code with that change:
const data = [{
x: 0.2,
y: [1, 2, 4]
},
{
x: 0.3,
y: [2]
},
{
x: 0.5,
y: [4, 7, 8, 12, 19]
}, {
x: 1.4,
y: [1, 3]
}
];
const newData = data.reduce(function(a, c) {
return a.concat(c.y.map(function(d) {
return {
x: c.x,
y: d
}
}));
}, []);
const x = d3.scaleLinear()
.domain([0, 2])
.range([0, 300]);
const y = d3.scaleLinear()
.domain([0, 20])
.range([0, 150]);
const svg = d3.select("svg");
svg.selectAll("circle")
.data(newData)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d.x)
})
.attr("cy", function(d) {
return y(d.y)
})
.attr("r", 4)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
Why not try unwrapping your data array before? That should be easy to process.
data = [{
x: 0.2,
y: [1, 2, 4]
},
{
x: 0.3,
y: [2]
},
{
x: 0.5,
y: [4, 7, 8, 12, 19],
},
{
x: 1.4,
y: [1, 3]
}
];
unwrappedData = [];
for (b in data) {
var temp = data[b].y.map((foo) => {
return {
x: data[b].x,
y: foo
}
})
unwrappedData = unwrappedData.concat(temp);
}
console.log(unwrappedData);
var svg = d3.select("body").append("svg")
.attr("width", 100)
.attr("height", 100)
.style("margin-top", "58px");
svg.selectAll("circle")
.data(unwrappedData)
.enter()
.append("circle")
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", 2)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Also, shouldn't the cx and cy attributes return d.x and d.y instead?
I want to update the circles and the paths in this graph with a transition. However it does not work.
I am not very experienced with D3. How can I make my code better? Changing data structure is no problem. I want bind the data to graph with exit() remove() and enter() without deleting whole graph and add the data every time I update again. I do not know how to use enter() exit() and remove() for nested data. One time for the whole group and the other side updating the circle and paths. The ID should be fixed.
Here I have a little single line example from d3noob.
here is a JS Fiddle
var data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 200
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
var svg = d3.select("svg");
var line = d3.line()
.x((d) => d.point.x)
.y((d) => d.point.y);
function updateGraph() {
console.log('dataset contains', data.length, 'item(s)')
var allGroup = svg.selectAll(".pathGroup").data(data, function(d) {
return d.id
});
var g = allGroup.enter()
.append("g")
.attr("class", "pathGroup")
allGroup.exit().remove()
g.append("path")
.attr("class", "line")
.attr("stroke", "red")
.attr("stroke-width", "1px")
.transition()
.duration(500)
.attr("d", function(d) {
return line(d.points)
});
g.selectAll("path")
.transition()
.duration(500)
.attr("d", function(d) {
return line(d.points)
});
g.selectAll("circle")
.data(d => d.points)
.enter()
.append("circle")
.attr("r", 4)
.attr("fill", "teal")
.attr("cx", function(d) {
return d.point.x
})
.attr("cy", function(d) {
return d.point.y
})
.exit().remove()
}
updateGraph()
document.getElementById('update').onclick = function(e) {
data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
updateGraph()
}
$('#cb1').click(function() {
if ($(this).is(':checked')) {
data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 200
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
} else {
data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
}];
}
updateGraph()
});
Edited to change the data join to be be appropriate.
I think the issue has to do with variable data. So, I added data as an argument to the function and specified separate names for different data sets. When I specify different data names, I'm able to update the chart:
var first_data = [{
id: 'p1',
points: [{
point: {
x: 100,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 200
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
var svg = d3.select("svg");
var line = d3.line()
.x((d) => d.point.x)
.y((d) => d.point.y);
function updateGraph(data) {
console.log('dataset contains', data.length, 'item(s)')
var allGroup = svg.selectAll(".pathGroup")
.data(data, function(d) { return d; });
var g = allGroup.enter()
.append("g")
.attr("class", "pathGroup")
allGroup.exit().remove()
g.append("path")
.attr("class", "line")
.attr("stroke", "red")
.attr("stroke-width", "1px")
.transition()
.duration(500)
.attr("d", function(d) {
console.log("update path");
return line(d.points)
});
g.selectAll("path")
.transition()
.duration(500)
.attr("d", function(d) {
return line(d.points)
});
g.selectAll("circle")
.data(d => d.points)
.enter()
.append("circle")
.attr("r", 4)
.attr("fill", "teal")
.attr("cx", function(d) {
return d.point.x
})
.attr("cy", function(d) {
return d.point.y
})
.exit().remove()
}
updateGraph(first_data)
document.getElementById('update').onclick = function(e) {
var update_data = [{
id: 'p1',
points: [{
point: {
x: 20,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
updateGraph(update_data)
}
$('#cb1').click(function() {
if ($(this).is(':checked')) {
var click_data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 200
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
} else {
var click_data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
}];
}
updateGraph(click_data)
});
Fiddle: https://jsfiddle.net/wj266kmx/32/
I'm having a lot of trouble getting a circle to delete. In this example I want to delete the circle labeled "2" but it just deletes the last circle in the array. If you uncomment the commented part and comment the graphNodes.splice(2, 1), it works as it should adding a new circle. But the delete won't work.
What am I missing here?
var width = 640,
height = 480;
var graphNodes = [
{ id: 0, x: 39, y: 343, r: 15 },
{ id: 1, x: 425, y: 38, r: 15 },
{ id: 2, x: 183, y: 417, r: 15 },
{ id: 3, x: 564, y: 31, r: 15 },
{ id: 4, x: 553, y: 351, r: 15 },
{ id: 5, x: 454, y: 298, r: 15 },
{ id: 6, x: 493, y: 123, r: 15 },
{ id: 7, x: 471, y: 427, r: 15 },
{ id: 8, x: 142, y: 154, r: 15 }
];
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
svg.append('rect')
.attr('class', 'graph')
.attr('width', width)
.attr('height', height)
.attr('fill', 'lightblue')
.attr('opacity', 0.3)
.on('click', function(){
/*graphNodes.push({
x: d3.mouse(this)[0],
y: d3.mouse(this)[1],
id: graphNodes.length,
r: 15
});*/
graphNodes.splice(2, 1);
update();
});
var nodeGroup = svg.selectAll('.nodes')
.data(graphNodes, function(d){ return d.id; })
.enter().append('g')
.attr('class', 'node');
nodeGroup.append('circle')
.attr('cx', function(d) { return d.x })
.attr('cy', function(d) { return d.y })
.attr("r", function(d){ return d.r; })
.attr("fill", "gray");
nodeGroup.append('text')
.attr("dx", function(d){ return d.x + 20; })
.attr("dy", function(d){ return d.y + 5; })
.text(function(d) { return d.id });
function update() {
if(nodeGroup){
// Update nodes
var node = nodeGroup.data(graphNodes),
nodeEnter = node.enter().append('g')
.attr('class', 'node');
nodeEnter.append('circle')
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.attr('r', function(d){ return d.r; })
.attr('fill', 'gray');
nodeEnter.append('text')
.attr("dx", function(d){ return d.x + 20; })
.attr("dy", function(d){ return d.y + 5; })
.text(function(d) { return d.id });
nodeGroup = nodeEnter.merge(node);
node.exit().remove();
}
}
Fiddle
When you re-bind your data in update function, you forgot your key function:
var node = nodeGroup.data(graphNodes, function(d){ return d.id; }),
Update fiddle.