I just started learning D3 and am having trouble understanding how to manipulate selections. My visualization is functioning very slowly and someone recommended to me to use Timeline to find the source of the problem. I looked over it and found that this snippet of code is most likely the culprit:
for (var i = 0; i < staticSvgLength; i++) {
var curritem = d3.select(svgChildren[0][i]);
if (curritem.attr("class") != "graphbutton") {
curritem.remove();
}
}
Here is a snapshot of the timeline that suggests to me that the problem is remove:
svgChildren elements are selected using:
var svgChildren = d3.selectAll(svgContainer[0][0].childNodes);
There are thousands of elements, most of which can be found within the following variable:
var rectangles = svgContainer.selectAll("svg")
.data(rawDataStore)
.enter()
.append("rect")
This is the graph I am trying to create:
The elements which I'm trying to prevent from being removed are the buttons in the upper left corner which are also stored in svgContainer. As a side question, is it bad practice to store the buttons in the same svg element I store the main graph?
EDIT: Here is where I define svgContainer:
var svgContainer = d3.select("body").append("svg")
.attr("Call", "svgContainer")
.attr("height", max_y)
.attr("width", max_x)
.append("g")
.attr("transform", "translate(" + margin.left + "," + (margin.top) + ")");
I select all the SVGs to be removed because when you click a button, it switches to a different graph. I don't want the two buttons to be removed, only the graphs, so I tried to exclude them from the selection by giving them a class and then removing all elements in svgContainer excluding the items that have the button class.
Related
I want to display multiple force directed graphs. Preferably with only using 1 svg element as well (as I think that increases performance as well as let me make a width and height for whole simulation as this may differ based on data).
My first thought and with some research I just did a for loop, re-evaluated my nodes/edges arrays and put that in a function that generates the force-directed graph.
for (var i = 0; i < sampleData.length; i++)
{
var nodes = [];
var edges = [];
x = x+100; //update position (i want to show grpahs side by side.)
root = sampleData[i];
nodes.push({"name": root, "x": x, "y": y, "fixed": true, "color": "purple"});
//These 2 recurisve functions generate my nodes and edges array just fine.
buildParents(childs, parents, test, counter);
buildChildren(childs, parents, test, counter);
//apply id to each node
nodes.forEach((d,i)=>d.generatedId='id'+i);
//build makes the force-directed layout.
build(nodes, edges);
}
This actually appears to work fine for me with my nodes and links. My issue is the text does not display for all nodes like it does if I only pass in one set of data. I have text defined as so in the force simulation:
var nodes_text = svg.selectAll(".nodetext")
.data(nodes)
.enter()
.append("text")
.attr("class", "nodetext slds-text-heading--label")
.attr("text-anchor", "middle")
.attr("dx", -20)
.attr("dy", 20)
.text(d=>d.name)
.attr('opacity',0)
I was able to reproduce this error by just making 2 arrays for nodes and 2 arrays for edges and passing it into a simulation. Here is a simple program that reproduces my error:
https://jsfiddle.net/mg8b46aj/9/
I think fixing this JFiddle would give me the right idea on how to put it in my program.
So I just call the build function twice (either order is error). The left node has text but one of them isn't the correct text field. Also dragging it around a little bit makes it "leave" its text behind. The right graph has nothing.
Edit: And clicking the a node on the right graph seems to reset the text positions.
The problem is with the d3 selector use for selecting labels. As you need two separate force layout diagrams, you should use a selector as shown below for labels.
var nodes_text = svg.append('g') //Append new group for labels in new diagram
.attr("class", "labels")
.selectAll(".nodetext")
.data(nodes)
.enter()
.append("text");
Updated Fiddle: https://jsfiddle.net/gilsha/qe7bbnwn/1/
I have a page that includes a dropdown menu to choose one of several bar charts to display. The page improperly reloads when the dropdown selection is changed. I have narrowed the culprit to this line:
$("svg").remove();
When I comment out that line, the HTML changes without the page reloading. But I need that line (or something similar), because I want the previous chart to go away when the new chart is selected.
I've also tried
d3.select("svg").remove();
but the same thing happens.
I've added
event.preventDefault();
but that doesn't help either.
I've made a jsfiddle to show my relevant HTML and JS/d3.
My page is here if seeing the whole thing will help. Note how the page reloads when the dropdown selection is changed.
(Update Jan. 3: I have followed the suggestions in the comments/answers below, but nothing has helped. I'm still having this problem.)
Nope, your page is not reloading. ( If it does reload you will see a spin icon in most of the browsers title). Like #Mansov told in the comment It is the margin that changes when you .remove() and add the svg.
I was having this same problem. Whenever I refreshed my chart, I was doing a .remove() to the entire svg reference, and then calling my build() function again.
In effect, what was happening was the <div> containing the SVG was collapsing to 0 height, causing the page to scroll back up to fit all the remaining content, and then when I rebuilt the chart the page would remain scrolled at the top.
So what I did was make a "frame" for the chart svg to sit on top of:
//===================================================== Initialize build
function initializeBuild() {
bilSVG = this.d3.select(rawSvg)
.append("svg");
calculateMargins();
bilSVG
.attr("width", width)
.attr("height", height)
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.style("opacity", .0)
;
}
and then created a group that held the chart:
//===================================================== build
function build() {
chartGroup = bilSVG
.append("g")
.attr("class","bil chartgroup")
.attr("transform", function(d) {
return "translate(" + width / 2 + "," + width / 2 + ")";
})
.attr("x", width / 2)
.attr("y", width / 2)
;
}
Then when I needed to rebuild the chart, I deleted the group, and not the whole SVG:
//===================================================== rebuild
function rebuild() {
chartGroup = bilSVG.select(".chartgroup");
chartGroup.remove();
build();
}
Hope this helps!
I'm trying to implement a slider in mpld3, much like this previous question.
I'm trying to build off of the draggable points example to do this. I'm having a bit of trouble understanding how the following bit of code works:
function dragged(d, i) {
d[0] = obj.ax.x.invert(d3.event.x);
d[1] = obj.ax.y.invert(d3.event.y);
d3.select(this)
.attr("transform", "translate(" + [d3.event.x,d3.event.y] + ")");
}
In particular, what does this refer to in this context. I originally thought that I could replace d3.select(this) with something like d3.select("#"+foo) where foo = this.props.id (at the top of the draw() function). But this doesn't work, as shown in this notebook I made. (The second piece of code doesn't allow you to drag the red dots around).
In case what I'm trying to do isn't clear... have a look at this notebook. I've made a plugin that allows the red square (the slider) to be dragged horizontally. What I would like to do is make dragging the red dot change the position of the blue dot. So I want to do something like:
function dragged(d, i) {
d[0] = obj.ax.x.invert(d3.event.x);
sliderPosition = obj.ax.x(d[0]);
targetPosition = obj.ax.x(-d[0]); // inverted sign
d3.select("#redsquare")
.attr("transform", "translate(" + [sliderPosition,sliderObj.ax.y(d[1])] + ")");
d3.select("#bluedot")
.attr("transform", "translate(" + [targetPosition,targetObj.ax.y(d[1])] + ")");
}
The intended behavior for this simple example is to have the blue dot move in the opposite direction of the red square when it is dragged. The question is, what do I put in place of "#redsquare" and "#bluedot"?
Many thanks.
I know a hacky way to do this. Instead of d3.select(this), you can find the specific element you are interested in the obj element array as follows:
d3.select(obj.elements()[0][i])
There must be a prettier way, though.
I am using dragdealer JS with D3.js. What i am doing is that when You drag the slider made by dragdealer JS the elements made by D3.js will move like a picture slider.
Here is the code which which i wrote : code.
Now there are two problems with this code:
1) This code is working in FireFox but not in Chrome & IE 10?
2) How to configure the slider so that on one slide, only one tile will move into the view and only one will move out?
The number of tiles or rectangles are not fixed. There can be any number of tiles depending on the user.
Code:
var width = 4000,
height = 200,
margin = 2,
nRect = 20,
rectWidth = (width - (nRect - 1) * margin) / nRect,
svg = d3.select('#chart').append('svg')
.attr('width', width);
var data = d3.range(nRect),
posScale = d3.scale.linear()
.domain(d3.extent(data))
.range([0, width - rectWidth]);
console.log(rectWidth)
svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('x', posScale)
.attr('width', rectWidth)
.attr('height', height);
function redraw(x)
{
svg.transition()
.ease("linear")
.attr("transform", "translate(" + -(x*rectWidth) + ")" );
console.log(-(x*rectWidth));
}
var step = nRect/2;
new Dragdealer('magnifier',
{
steps: step,
snap: true,
animationCallback: function(x, y)
{ console.log(x*10)
redraw(x*step);
}
});
i am trying to devise a way so that the value of steps will change according to the number of tiles.
Please help me.
You had a few problems that I've fixed here: http://jsfiddle.net/SqKZv/1/
In Chrome your svg element needed the height property set
In Chrome/IE, it doesn't appear that you can apply the transform attribute to your SVG element, I'm actually surprised this works in FireFox. I wrapped all of your rect elements in a g element and transformed that.
D3 does dragging very well by itself, so you don't need Dragdealer to do this. In addition to d3.behavior.drag, you can check out d3.svg.brush, specifically these examples of snapping to get what you want:
Brush Snapping http://bl.ocks.org/mbostock/6232537
Brush Snapping II http://bl.ocks.org/mbostock/6232620
You may also want to try out the new D3 feature called brush: https://github.com/mbostock/d3/wiki/SVG-Controls
Here is an example I made using brush to implement a similar feature as you mentioned.
https://github.com/CSE512-14W/a3-chaoyu-aniket
I have a piece of JavaScript code which creates (using D3.js) an svg element which contains a chart. I want to update the chart based on new data coming from a web service using AJAX, the problem is that each time I click on the update button, it generates a new svg, so I want to remove the old one or update its content.
Here is a snippet from the JavaScript function where I create the svg:
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
How can I remove the old svg element or at least replace its content?
Here is the solution:
d3.select("svg").remove();
This is a remove function provided by D3.js.
If you want to get rid of all children,
svg.selectAll("*").remove();
will remove all content associated with the svg.
Setting the id attribute when appending the svg element can also let d3 select so remove() later on this element by id :
var svg = d3.select("theParentElement").append("svg")
.attr("id","the_SVG_ID")
.attr("width",...
...
d3.select("#the_SVG_ID").remove();
I had two charts.
<div id="barChart"></div>
<div id="bubbleChart"></div>
This removed all charts.
d3.select("svg").remove();
This worked for removing the existing bar chart, but then I couldn't re-add the bar chart after
d3.select("#barChart").remove();
Tried this. It not only let me remove the existing bar chart, but also let me re-add a new bar chart.
d3.select("#barChart").select("svg").remove();
var svg = d3.select('#barChart')
.append('svg')
.attr('width', width + margins.left + margins.right)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
Not sure if this is the correct way to remove, and re-add a chart in d3. It worked in Chrome, but have not tested in IE.
I am using the SVG using D3.js and i had the same issue.
I used this code for removing the previous svg but the linear gradient inside SVG were not coming in IE
$("#container_div_id").html("");
then I wrote the below code to resolve the issue
$('container_div_id g').remove();
$('#container_div_id path').remove();
here i am removing the previous g and path inside the SVG, replacing with the new one.
Keeping my linear gradient inside SVG tags in the static content and then I called the above code, This works in IE
You could also just use jQuery to remove the contents of the div that contains your svg.
$("#container_div_id").html("");
You should use append("svg:svg"), not append("svg") so that D3 makes the element with the correct 'namespace' if you're using xhtml.
I follow the code from https://www.d3-graph-gallery.com/graph/line_basic.html and had this code:
svg.append("path")
.datum(filteredData)
.attr("fill", "none")
.attr("stroke", "blue")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function(k) { return x(k.date) })
.y(function(k) { return y(k.value) })
)
So for me it turned out to be that I slap this right before the generation of new line:
d3.selectAll("path").remove()