How to create D3 svg element in drag and drop method - d3.js

I am have a pallet section using html and SVG canvas.I want to create SVG element when click a button and paste it in to SVG in drag and drop method.my pallet section i creates like this
<div id="toolbox">
<input id="task-button" type="image" title="Activity" src="img/rec.png" alt="Activity">
</div>
I create SVG element like this when button click
d3.select("#task-button").on("click", function(){
var sampleSVG = svg;
sampleSVG.append('rect')
.attr('id', 'task'+(++idtaskelement))
.style("stroke", "black")
.style("stroke-width", "2")
.style("fill", "white")
.attr('transform', 'translate(' + d3.event.pageX + ',' + d3.event.pageY+ ')')
.attr("rx", 10)
.attr("ry", 10)
.attr("width", 120)
.attr("height", 80)
.on("mouseover", function(){d3.select(this).style("fill", "aliceblue");
var point = d3.mouse(this)
, p = {mx: point[0], my: point[1] };
console.log(p.mx +"and "+ p.my);
})
.on("mouseout", function(){d3.select(this).style("fill", "white");})
.on("click", function(){
tmodal.style.display = "block";
})
.call(drag)
});
This element have drag functionality i created like this.
var drag = d3.behavior.drag().on('drag', function(d) {
dragMove(this)
})
function dragMove(me) {
var x = d3.event.x
var y = d3.event.y
d3.select(me).attr('transform', 'translate(' + x + ',' + y + ')')
}
Is there way to call Drag function and start dragging after button click and when we click in canvas we can paste it in the canvas.
If there any way to do this functionality please let me know.

Related

How can i display tooltip at the top of each vertical bar

I have tried writing couple of equations for the same but unabe to get it aligned well. I need to display tooltip at the top of each bar.
Here is fiddle of the same
I am using mouseover events to display tooltips
sets.append("rect")
.attr("class","global")
.attr("width", xScale.rangeBand()/2)
.attr('y', function(d) {
return yScale((d.global/total)*100);
})
.attr("height", function(d){
return h - yScale((d.global/total)*100);
})
.attr('fill', function (d, i) {
return color(d.global);
})
.on('mouseover', function(d, i) {
var xPos = xScale.rangeBand()*i;
//console.log(xScale(i)); 6 190 282
console.log(xScale.rangeBand()*i);
var yPos = yScale((d.global / total) * 100);
d3.select('#hor_tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.style('display', 'block')
.html(d.global);
})
.on('mouseout', function() {
d3.select('#hor_tooltip').style('display', 'none');
})
.append("text")
.text(function(d) {
return commaFormat((d.global/total)*100);
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
You should use d3.event inside the mouseover event handler. You can use the x,y coordinates of the event or request the bounding box of the evnet target element.
The following code should help but you still need to adjust for the height of the tootip itself:
var px = d3.event.pageX;
var py = d3.event.target.getBoundingClientRect().top;
You could also try foxTooltip.js. It has an option to always position on a specific side of an element.
https://github.com/MichaelRFox/foxToolTip.js

D3 JS appending tooltip to each data circle point and finding on hover event for svg element

I have an array of objects.These objects contain a set of attributes named "x" and "y". Now as i iterate through the array,i append circle for each object into array taking these coordinates as center of the circle.I am trying to attach a tooltip on mouse hover of each circle which displays the x and y coordinate of the circle.
The two problems i am facing is
1.How to append a div element to each circle
2.How to get hover event for the circle?
Please help?
<!doctype html>
<html>
<head>
<title>D3 Basics</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
</head>
<body>
<script>
var data=[
{"x":"100","y":"20"},
{"x":"102","y":"22"},
{"x":"200","y":"30"},
{"x":"500","y":"40"},
{"x":"500","y":"30"}
];
var svgHeight=800;
var svgWidth=800;
var margin=50;
var divelement=d3.select("body")
.append("div")
.attr("height",svgHeight)
.attr("width",svgWidth)
.attr("style","border:1px solid black;");
var svgElement=divelement.append("svg")
.attr("height",svgHeight)
.attr("width",svgWidth);
var boxGroupElement=svgElement.append("g")
.attr("transform","translate("+margin+","+margin+")");
//appending data circle points
for (var a=0; a<data.length; a++) {
boxGroupElement.append("circle")
.attr("cx",data[a].x)
.attr("cy",data[a].y)
.attr("r","3")
.attr("fill","yellow")
.attr("stroke","blue");
}
</script>
</body>
</html>
I have marked this as duplicate but there can be something to learn here.
Where you are creating the circles :
for (var a=0; a<data.length; a++) {
boxGroupElement.append("circle")
.attr("cx",data[a].x)
.attr("cy",data[a].y)
.attr("r","3")
.attr("fill","yellow")
.attr("stroke","blue");
}
This is totally wrong if you are using D3. If you put a mouseover event on this like you normally would with D3 to console log the data like so :
.on('mouseover',function(d){ console.log(d);});
This won't work as there is no data appended to your selection. A work around would be the following :
var circles = boxGroupElement.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", "3")
.attr("fill", "yellow")
.attr("stroke", "blue")
Now for the tooltip. I have referenced this above : Show data on mouseover of circle
The second answer precisely. Here is how it works :
Add a div for your tooltip to sit and set visibility to hidden (so it can become visible on mouseover) :
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
And on mouseover a node show data. Here I have shown the position. Bare in mind, this way wouldn't work if you stuck with your original way of creating the circles as you wouldn't be able to find the data :
.on("mouseover", function(d) {
tooltip.text("Pos : " + d.x + ' : ' + d.y);
return tooltip.style("visibility", "visible");
})
.on("mousemove", function() {
return tooltip.style("top",
(d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px");
})
.on("mouseout", function() {
return tooltip.style("visibility", "hidden");
});
Working fiddle : https://jsfiddle.net/thatOneGuy/7qt1aoan/2/
EDIT
If you are adamant you want to keep it like you have the following could be a work around :
.on("mouseover", function(d, i) {
tooltip.text("Pos : " + data[i].x + ' : ' + data[i].y);
return tooltip.style("visibility", "visible");
})
.on("mousemove", function() {
return tooltip.style("top",
(d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px");
})
.on("mouseout", function() {
return tooltip.style("visibility", "hidden");
});
Updated fiddle for this workaround : https://jsfiddle.net/thatOneGuy/7qt1aoan/3/
Again on the mouseover, you can't use data[a].x as this will always return the last element of the data, closures etc, so I use data[i].x which gives you the current circle you are mousing over :)
You need to add mouseover event to boxGroupElement.append("circle") something like this
boxGroupElement.append("circle")
.attr("cx",data[a].x)
.attr("cy",data[a].y)
.attr("r","3")
.attr("fill","yellow")
.attr("stroke","blue").on("mouseover", function(d) {
div.transition().duration(100).style("opacity", .9);
div.html("My Tooltip" + "<br/>" +d )
.style("left", (d3.event.pageX) + "px")
.style("top",(d3.event.pageY - 28) + "px")
.attr('r', 8);
d3.select(this).attr('r', 8)})
.on("mouseout", function(d) {
div.transition().duration(600).style("opacity", 0)
d3.select(this).attr('r', 3);
});
Here is a working example.

D3 Map - Context Menu - not able to update path attributes

Hi I am trying to add markers, (or changing fill property is also fine) of the D3 map based on Context menu selection. I was referring to the examples at http://jsfiddle.net/1mo3vmja/2/, (pasting the code below) but after clicking the context menu item , I am not able to get the d3 element which invoked the context menu. Can someone help?
var fruits = ["Apple", "Orange", "Banana", "Grape"];
var svgContainer = d3.select("body")
.append("svg")
.attr("width", 200)
.attr("height", 200);
var circle = svgContainer
.append("circle")
.attr("cx", 30)
.attr("cy", 30)
.attr("r", 20)
.on('contextmenu', function(d,i) {
// create the div element that will hold the context menu
d3.selectAll('.context-menu').data([1])
.enter()
.append('div')
.attr('class', 'context-menu');
// close menu
d3.select('body').on('click.context-menu', function() {
d3.select('.context-menu').style('display', 'none');
});
// this gets executed when a contextmenu event occurs
d3.selectAll('.context-menu')
.html('')
.append('ul')
.selectAll('li')
.data(fruits).enter()
.append('li')
.on('click' , function(d) { console.log(d); return d; })
.text(function(d) { return d; });
d3.select('.context-menu').style('display', 'none');
// show the context menu
d3.select('.context-menu')
.style('left', (d3.event.pageX - 2) + 'px')
.style('top', (d3.event.pageY - 2) + 'px')
.style('display', 'block');
d3.event.preventDefault();
});
You can store the element on which you are drawing the context menu something like this:
.on('contextmenu', function(d, i) {
var me = this;//storing the circle instance inside variable me
Now when you select the context menu element you can refer the object me.
.on('click', function(d) {
console.log(d, me);
return d;
})
Working example here
Hope this helps!

How to move elements along with svg group

I have a circle that gets appended on drag drop. I want the circle to move along with group when I move the group around with mouse
Here's what I have tried which isn't working:
//targetG is the group element
targetG.append("rect")
.attr("fill", "none")
.style("stroke", "black")
.style("stroke-width", "2px")
.attr("width", 200)
.attr("height", 200)
.style("fill", "white")
.call(
d3.behavior.drag()
.on('drag', moveRect).origin(function () {
var t = d3.select(this);
return {x: t.attr("x"), y: t.attr("y")};
}));
Here's the full code in fiddle: http://jsfiddle.net/vk62y7un/
There's a couple of small problems that need to be fixed.
Your translate component split function is splitting by ,.
translate = (translate.substring(0, translate.indexOf(")"))).split(",");
While this works in Chrome, it should be splitting by space for IE.
translate = (translate.substring(0, translate.indexOf(")"))).split(",");
if (translate.length === 1)
translate = translate[0].split(' ');
The circle wasn't being attached to the g because of this.
Your (container) drag event is attached to the rectangle inside the g. This should be attached to the g instead. Correspondingly the drag functions should manipulate the transform (translate) attribute and not the x and y.
var targetG = svg.append("g")
.attr("transform", "translate(150,150)")
.call(
d3.behavior.drag()
.on('drag', moveRect).origin(function () {
...
and
function moveRect() {
d3.select(this)
.attr('transform', 'translate(' + d3.event.x + ' ' + d3.event.y +')');
}
The origin (for the g now) should be the (parsed) transform (translate) attribute at the start of the drag.
....
var tc = d3.select(this).attr('transform').replace(/[a-z()]/g, '').split(' ');
if (tc.length === 1)
tc = tc[0].split(',')
return { x: Number(tc[0]), y: Number(tc[1]) };
}));
Notice, the ===1 check and split - that's so that it works in IE and Chrome.
Fiddle (works in IE and Chrome) - http://jsfiddle.net/3hyu6om8/
The problem is when you try to drag rectangle you don't select circles. I made some changes and you could drag circles along rectangle.
Add this part to your code:
var groupAll = d3.behavior.drag()
.origin(Object)
.on("drag", function(d, i) {
var child = this;
var move = d3.transform(child.getAttribute("transform")).translate;
var x = d3.event.dx + move[0];
var y = d3.event.dy + move[1];
d3.select(child).attr("transform", "translate(" + x + "," + y + ")");
});
Complete Code here.

D3 transform transition "null parsing transform attribute"

The basic problem is that I have a drag event on a path that transitions the path a certain distance in the x axis.
When another event happens I want to return the path back to its original position before the drag event.
I tried to do this with the below code:
svg.select('#' + uniqueid + '-flare')
.select('path')
.transition().duration(1000)
.ease('linear')
.attr('transform', 'translate(0,0)');
This is giving me the warning "null parsing transform attribute" and the SVG container in the HTML shows transform="".
Using this following code works perfectly well, but is choppy. I want the smooth animation provided by the transition.
svg.select('#' + uniqueid + '-flare')
.select('path')
.attr('transform', 'translate(0,0)');
Any ideas? Thanks!
Update
Lars asked to see some more code. So.... (It is very abridged)
var dragtype, dragsum;
var wordDrag = d3.behavior.drag()
.origin(Object)
.on('drag', function (d) {
if(dragType != d.word) {
dragType = d.word;
dragSum = 0;
}
//update current position
dragSum = dragSum + parseInt(d3.event.dx);
var xMovement = parseInt(this.getAttribute('x')) + parseInt(d3.event.dx);
d3.select(this)
.attr('class', 'dragged-points')
.attr('x', xMovement);
if(uniqueId == d.word) {
svg.select('#' + uniqueId + '-flare')
.select('path')
.attr('transform', 'translate(' + dragSum + ',0)');
}
}
});
word.selectAll('text')
.data(data)
.enter().append('text')
.attr('class', 'points')
.attr('id', ... )
.attr('x', ...)
.attr('y', ...)
.call(wordDrag);
tick = setInterval(function(){
animate();
}, 1000);
function animate() {
word.transition()
.duration(1000)
.ease('linear')
.attr('x', ...)
.attr('y', ...);
svg.select('#' + uniqueId + '-flare')
.select('path')
.transition().duration(1000)
.ease('linear')
.attr('transform', 'translate(0,0)');
}

Resources