Making svg container appear one below another on array looping - javascript

I have the following code where I created two svg container of different height and width and it is created for every element in the array. The code works well but I want the svg container text1 which contains title1 to appear below the svg container text2 that displays the title2 rather than side by side that's how it appears now, i.e., next to each other. How to make container 2 to appear just below the container 1
Here is the code
function draw(data) {
data.forEach(function(d) {
var text1 = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 100);
var title1 = d.values.title1;
text1.append("text")
.attr("x", "50%")
.attr("y", "10%")
.text(title1);
var text2 = d3.select("body").append("svg")
.attr("width", 300)
.attr("height", 500);
var title2 = d.values.title2;
text2.append("text")
.attr("x", "40%")
.attr("y", "10%")
.text(title2);
});
}

You probably can solve your problems just changing your CSS. By default, SVGs will display side by side, if there is enough room in the page. In this snippet, 5 SVGs are produced (click "run code snippet"):
var data = d3.range(5);
var body = d3.select("body");
var svg = body.selectAll("svg")
.data(data)
.enter()
.append("svg")
.attr("width", 100)
.attr("height", 100);
svg {
background-color: teal;
margin-right: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
This is exactly the same code, but setting the display in the CSS:
display: block;
Check the difference (click "run code snippet"):
var data = d3.range(5);
var body = d3.select("body");
var svg = body.selectAll("svg")
.data(data)
.enter()
.append("svg")
.attr("width", 100)
.attr("height", 100);
svg {
background-color: teal;
display: block;
margin-bottom: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Related

D3.js - function don't listen for an EventListener who resize the svg

i'm quite new with javascript and D3js and I wrap my head on somes issues,
I created a function to resize the svg and return the svg selection
const bodyClient = {
width: document.body.clientWidth,
height: document.body.clientHeight,
};
const container = d3.select("body");
function resizeSVG(container, bodyClient) {
let svg = container.selectAll("svg").data([null]);
svg = svg
.enter()
.append("svg")
.merge(svg)
.attr("width", `${bodyClient.width}`)
.attr("height", `${bodyClient.height}`);
return svg;
}
Attach to an EventListener on window:
function renderingResize() {
return resizeSVG(container, bodyClient);
}
const svg = renderingResize();
window.addEventListener("resize", renderingResize());
And after a create a function who display a half PieChart and take this width and height from svg:
halfCircle(svg, schoolarTimeStamp, 250, 10, 180, 0, true);
It's like the function isn't bind to the EventListener because when I append a rect outside the function he react, and for the function only when i resfresh the page any idea?
Please find below a simple example of resize with D3, the svg is like a div with an overflow hidden property, so for a semi-circle you can play cx property
const svg = d3.select("body")
.append("svg")
.attr('style', 'background:red')
.attr("width",window.innerWidth)
.attr("height",window.innerHeight);
svg.append('circle')
.attr("fill", "white")
.attr("cx", 0)
.attr("cy", 90)
.attr("r", 90)
window.addEventListener("resize", function(){
d3.select("svg")
.attr("width",window.innerWidth)
.attr("height", window.innerHeight);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

How to center a text on a svg image using d3.js?

I am trying to put a text so that it is located in the center of my element as in the following image:
I would like this to be done dynamically depending on the size of my image.
How can I do it?
<body>
<script type="text/javascript">
const svg = d3.select("body").append("svg").attr("width",1000).attr("height",1000);
var widthMarker=50;
var img = svg.append("svg:image")
.attr("xlink:href", "marker.svg")
.attr("width", widthMarker)
.attr("height", widthMarker)
.attr("x", 228)
.attr("y",53);
svg.append("text").attr("width",100).attr("height",100).text("hello world");
</script>
</body>
http://plnkr.co/edit/39zEvnjOmotZXYF2GFwq?p=preview
You can simply put the image and text in a group container and adjust the text position relatively as shown below.
Plunker
const svg = d3.select("body")
.append("svg")
.attr("width", 1000)
.attr("height", 1000);
var widthMarker = 50;
var imgContainer = svg.append("g")
.attr("transform", "translate(228,53)");
var img = imgContainer.append("svg:image")
.attr("xlink:href", "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQgfLDf_0gBVxWp_7ec2UR3Dro5rBXquElgRdSH6K8LQQ7QanSQ")
.attr("width", widthMarker)
.attr("height", widthMarker);
var text = imgContainer.append("svg:text")
.attr("dy", widthMarker/2)
.text("hello world");
text.attr("dx", (widthMarker - text.node().getComputedTextLength()) / 2);
<script src="https://d3js.org/d3.v3.min.js"></script>

Replace old content d3

I am creating a div container which I then fill with a svg
var someContainer = d3.select("#abc")
.append("div")
.classed("svg-container", true)
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox","0 0 400 100")
.classed("svg-content-responsive", true)
.selectAll("circle")
.data(someScale.range());
I then append my data to it
someContainer.remove();
someContainer.enter()
.append("circle")
.attr("x", function (d, i) {
return i * 2;
})
.attr("y", 5)
.attr("height", 15)
....;
However, whenever I update the content of the svg, i.e. append new circles, a completely new div-container and svg-container gets created. That leaves me with the old data visually staying in its place and right on the bottom (meaning, 100px further down) there is the new data. Its basically a visual copy, but with the new data... Whenever I udpate my data, a new coy gets replaced under the old one, leaving me with n graphics.
Here is the css that styles the relative container and makes sure, it scales when the window size is changed. Source: Resize svg when window is resized in d3.js */
.svg-container {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 50%; /* aspect ratio */
vertical-align: top;
overflow: hidden;
}
.svg-content-responsive {
display: inline-block;
position: absolute;
top: 10px;
left: 0;
}
Any ideas what I am doing wrong?
If you just need to delete all the old circles you can do it as follows:
someContainer.selectAll("circle").remove()
And then add new circles by data -> enter -> append sequence.
someContainer.selectAll("circle")
.data(new_circle_data)
.enter()
.append("circle")
.attr("x", function (d, i) {
return i * 2;
})
.attr("y", 5)
.attr("height", 15)
....;
If you only want to delete some of the existing circles and keep the others you can use the general update pattern. You need to do something like this:
var circleUpdate = someContainer.selectAll("circle")
.data(new_circle_data)
circleUpdate.enter().append("circle")
.attr("x", function (d, i) {
return i * 2;
})
.attr("y", 5)
.attr("height", 15)
....;
circleUpdate.exit().remove()
The problem is that each time the data gets updated, a new div gets created.
var someContainer = d3.select("#abc")
.append("div")
.classed("svg-container", true)
...
In order to update the data, the div needs to be replaced instead of creating a new div each time the data changes.
That can be done by adding this line
d3.select(".svg-container").remove();
above the var someContainer = d3.select...

how to rotate any shape continuously in D3 js?

I am trying to call the update function to rotate the text by 1 degree and once the degree reaches 360 again the rotation angle becomes 0 and hence it will keep on rotating. But I think this is not the right way to approach the problem also it is not working. So suggest me the way to do it if anyone know it.
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var width = 600;
var height = 300;
var holder = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// draw the text
holder.append("text")
.style("fill", "black")
.style("font-size", "56px")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", "translate(300,150) rotate(0)")
.text("Hi, how r u doing");
// Initial starting angle of the text
for(var i=0;i<=360;i++){
update(i);
if(i==360){i=0;}
}
var n;
// update the element
function update(n) {
// rotate the text
holder.select("text")
.transition()
.duration(2000)
.attr("transform", "translate(300,150) rotate("+n+")");
}
</script>
</body>
</html>
Example JS Fiddle here.
Your for loop never ends as you reset the counter i to 0 just before it finishes. If you remove this line, the code will have no visible result as the for loop executes so quickly, it's already completed before you can see anything.
A better solution is to use setInterval e.g.
var width = 600;
var height = 300;
var holder = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// draw the text
holder.append("text")
.style("fill", "black")
.style("font-size", "56px")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", "translate(300,150) rotate(0)")
.text("Hi, how r u doing");
// Initial starting angle of the text
var i = 0;
var timeInterval = 10;
setInterval(function(){
i += 1;
update(i % 360)
},timeInterval);
var n;
// update the element
function update(n) {
// rotate the text
holder.select("text")
.attr("transform", "translate(300,150) rotate("+n+")");
}
You can control the speed by adjusting the timeInterval variable.
I've added an example JS Fiddle here.

3d.js (Force) - get current mouse position on the link for the tooltip

I am trying to enable the tooltip when the mouse is on the line. This is the current setup that I have:
HTML:
<div id="graphContainer"></div>
<div id='hoveringTooltip' style='position:fixed;'></div>
3d.js Code - Basic Setup:
var width = 1200,
height = 900;
var svg = d3.select("#graphContainer").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.charge(-120)
.linkDistance(80)
.size([width, height]);
//Create all the line svgs but without locations yet
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function (link) {
return link.thick
})
.attr("data-info", function (link) {
return link.info;
})
.style("marker-end", "url(#suit)")
.on("mouseover", mouseOverLink)
function mouseOverLink (e) {
//d3.select(this).style("stroke","red");
d3.select(this).attr("class", "link_selected");
var that = this;
var value = Number( this.attributes.x1.value );
var xx = d3.select(this).attr("cx") + "px"
var yy = d3.select(this).attr("cy") + "px"
var xxx = d3.event.pageX;
var yyy = d3.event.pageY;
var coordinates = [0, 0];
coordinates = d3.mouse(this);
var x = coordinates[0];
var y = coordinates[1];
var value = this.attributes[1].value;
$('#hoveringTooltip').show();
$('#hoveringTooltip').html(value);
$('#hoveringTooltip').css({
"top": xxx,
"left": yyy
});
}
In the mouseOverLink function, I have tried all scenarios that I could find on SO and on internet. I do get the values for the X/Y mouse, but they are always wrong. I also tried to attach mouseover link using Jquery eventing, but those values are also wrong.
If there is another way to display the toolitip on the link, I would be more then happy.
Since you did not provide a working fiddle so I have made a force-directed Plunk to explain the solution.
First give style for the tool tip div like this:
div.tooltip {
position: absolute;// because we are going to give the position.
text-align: center;
width: 160px;
height: 50px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
Next make a div and append it to the body like this:
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);//so that its not visible initially
Now on link mouse over/mouse out do
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.on("mouseover", function (d, i) {//show tooltip
tooltip.transition()
.duration(200)
.style("opacity", 0.9);
tooltip.html("<p>source:"+d.source.name+ "</p><p>target:"+d.target.name+ "</p>")//tool tip html
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function (d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);//hde tooltip
})
Working example here.
Hover over the link to see the tool tip.
My first thought is if you're using xxx and yyy in a css style rule, you need to add "px" to the end of the values like you do for xx and yy

Categories

Resources