I can access d3.event.x from inside on("drag"...) but x doesn't exist inside on("dragstart"...); why? And how can I get it another way, preferably not very hackish?
Check console output for this example:
d3.select("body").append("svg").append("rect")
.attr("width", 100)
.attr("height", 100)
.style("color", "black")
.call(
d3.behavior.drag()
.on("dragstart", function(){
console.log(d3.event);
})
.on("drag", function(){
console.log(d3.event);
})
.on("dragend", function(){
console.log(d3.event);
})
);
JSFiddle
#Lars explains why, so how do you do it? d3.mouse is pretty slick:
d3.behavior.drag()
.on("dragstart", function(){
console.log('relative to rect: ' + d3.mouse(this));
console.log('relative to body: ' + d3.mouse(d3.select("body").node()));
})
Updated example.
This is the intended behaviour (see the documentation):
Drag events (but not dragstart and dragend events) expose "x" and "y" properties representing the current position of the drag gesture in local coordinates.
The start and end events are fired before and after the actual drag takes place, i.e. there's no coordinate change compared to the first and last drag events.
Related
I face a problem with context-event and its parameter.
Following to versions are used in my site, to attach events:
This approach is used for simple graphic Elements that get their events after loading from database:
const graph = editor.append("g")
.attr("id", "a" + graphic.Id.toString())
.html(graphic.SvgString)
.on("mouseover", graphicMouseOver)
.on("mouseout", graphicMouseOut)
.on("mousedown", graphicMouseDown)
.on("contextmenu", onGraphicContext)
.call(d3.drag()
.on("start", graphicDragStart)
.on("drag", graphicDragging)
.on("end", graphicDragEnd));
Then I have a different approach for some kind of symbols, Ioad to my editor:
const g = editor.append("g")
.attr("transform", "translate(" + symbol.SymbolPosition + ")")
.attr("id", subFunctionId.toString())
.attr("class", "draggable preview")
.attr("pointer-events", "fill")
.call(d3.drag()
.on("start", symbolDragStart)
.on("drag", symbolDragging)
.on("end", symbolDragEnd)
);
Depending on some circumstance, I attach some events to these "symbols" later:
function addSymbolEvents(svgSymbols) {
log.debug("addSymbolEvents");
for (var i = 0; i < svgSymbols.length; i++) {
svgSymbols[i].addEventListener('mouseenter', symbolMouseEnter);
svgSymbols[i].addEventListener('mouseover', symbolMouseOver);
svgSymbols[i].addEventListener('mouseout', symbolMouseOut);
svgSymbols[i].addEventListener('pointerdown', symbolMouseDown);
svgSymbols[i].addEventListener('dblclick', symbolDblClick);
svgSymbols[i].addEventListener('contextmenu', symbolRightClick);
}
return svgSymbols;
}
If I log the event-parameter the following way, I get undefined on onGraphicContext and a complete MouseEvent-object on symbolRightClick
function onGraphicContext(evt) {
console.log("onGraphicContext", evt);
}
function symbolRightClick(evt) {
console.log("symbolRightClick", evt);
}
What is the difference here?
Isn't the d3.js-.on-attribute the same as addEventListener?
Thanks Carsten
selection.on() is a D3 method, not native JavaScript. Internally, selection.on() uses addEventListener, as you can see in the source code.
However, selection.on() is not just a wrapper for addEventListener. As the API says, when you attach a listener using selection.on(), that listener...
...will be evaluated for the element, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i])
Because of that, selection.on() is way more handy for D3 programmers than just using the vanilla addEventListener.
Ok, I found the solution.
As #Teemu wrote it's obviously not the same.
So if you use .on("contextmenu", onGraphicContext), you need to work with the d3.event-object in the event function:
function onGraphicContext() {
console.log("onGraphicContext", d3.event);
}
Typing d3.behavior.drag() has me confused. I'd like to specify drag types so that different objects trigger different drag events. I'm used to programming with D3 dispatches/namespaces doing the following:
registering them with the D3 dispatch object
then using something like .on('click.typeHere') to listen for the dispatch event
and using dispatch.typeHere(<<data>>) to trigger the event.
I can't figure out how the normal dispatch procedure applies to drag behavior. For example:
var svg = d3.select('svg').append('g')
.attr('transform','translate(0,20)');
svg.append('rect')
.attr('height', 50)
.attr('width', 20)
.style('fill', 'steelblue');
var drag = d3.behavior.drag();
drag.on('drag', function() {
console.log('drag');
}).on('drag.type', function() {
console.log('namespace active');
}).on('drag.type2', function() {
console.log('namespace2 active');
});
svg.append('circle')
.attr('cx',20)
.attr('cy',0)
.attr('r',10)
.style('fill', 'orange')
.call(drag);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<svg></svg>
I want the circle to trigger just drag.type when it is dragged and not the other drags that are registered in the drag behavior namespace. But .call(drag.type) isn't supported. What is the right way to go about this?
I would use different drag behaviours for the different types of objects with the respective drag handlers doing the appropriate thing. This will save you having to mess around with event namespaces:
var drag1 = d3.behavior.drag()
.on('drag', function() {
console.log('drag rect');
});
svg.append('rect').call(drag1);
var drag = d3.behavior.drag()
.on('drag', function() {
console.log('drag circle');
});
svg.append('circle').call(drag);
Complete demo here.
I am currently trying to implement a zooming feature that is slightly different from the one that already exists.
Actually, I would like that if a user clicks and drags on the graph it zooms on the so defined domain. I would like to do it that way because with the mouse wheel it prevents the user from the page up/down.
As it doesn't seem to be possible with the C3.js API, I tried to implement the drag event by following this little walkthrough on D3.js Drag Behaviour.
However, I didn't understand it well as it is not working when I try it on the graph.
Here's a sample of my code :
function setHandlers() {
/**
* A custom drag event to zoom on the graph
*/
var drag = d3.behavior.drag();
d3.selectAll('.c3-event-rects').on(".drag", null);
drag
.on('dragstart', function (d) {
d3.event.sourceEvent.stopPropagation();
console.log("start");
console.log(d)
})
.on('drag', function (d) {
console.log("on bouge :)")
})
.on('dragend', function (d) {
console.log("end");
console.log(d)
})
}
I call this function whenever I refresh my graph and I have already coded a custom handler for a double click (in the same function but I have it off to be more clear). I would like to know how to successfully trigger a drag event in a C3.js graph, especially the dragstart and dragend events?
c3-event-rects marks the layer that drives the events (tooltips, clicks, etc.). You don't usually want to move this unless you want the event reactions to be offset (like you get the bar 1 tooltips when you hover over someplace else than over bar 1)
Also, the layer has it's opacity set to 0, so you can't actually see it move unless you override it's opacity. Something like
.c3-event-rects {
fill: red;
fill-opacity: 0.2 !important;
}
That said, here is how you do this
var drag = d3.behavior.drag()
.origin(function () {
var t = d3.select(this);
var translate = d3.transform(t.attr('transform')).translate;
return { x: translate[0], y: translate[1]};
})
.on("drag", function () {
d3.select(this)
.attr("transform", "translate(" + d3.event.x + " " + d3.event.y + ")")
})
d3.selectAll('.c3-event-rects').call(drag);
Fiddle - http://jsfiddle.net/d5bhwwLh/
Here's how it looks - the red spot is where I had my mouse over to make the tooltip appear for the first set of bars
Here is an svg with a circle drawn on it:
svg = d3.select("body").append("svg")
.on("mouseover", function() { console.log("callback");} );
svg.append("circle")
.attr("cx", 50)
.attr("cy",50)
.attr("r",20)
.attr("fill","red");
Why does mouseover fire when I mouse over the circle? I assume because its a child element of its parent svg?
But I'd like to inhibit this action. How can I do this?
The arguments on the mouseover function don't seem to pass in the event, so I came up with this solution instead.
svg = d3.select("body").append("svg")
.on("mouseover", function() {
var event = window.event;
if (event.target.nodeName === "svg") {
console.log("callback");
}
});
svg.append("circle")
.attr("cx", 50)
.attr("cy",50)
.attr("r",20)
.attr("fill","red");
If you want to inhibit pointer interactions set pointer-events="none" on that element.
.attr("pointer-events","none")
Container elements don't trigger mouse events in SVG. The event will only get triggered when you mouse over any of the child graphical elements it contains.
Ok so I have the following code example where I have circles in an svg element. Each circle has a click event and I'm trying to animate the circle that was clicked. Currently all circles animate because I'm referring to the bubble object. What I want is to refer to the clicked object its self and not the other ones:
var data_items=[100,200,300];
var svg = d3.select("#chart")
.append("svg").attr("width", 800).attr("height", 600);
var g = svg.selectAll(".bubbleContainer")
.data(data_items)
.enter().append("g")
.attr("class","bubbleContainer");
var bubble = g.append("circle")
.attr("class","bubble")
.attr("cx", function(d) {
return d;
})
.attr("cy", function(d) {
return d
})
.attr("r", function(d) {
return d/2
})
.on("click",function(d){
bubble
.transition()
.duration(1000)
.attr("r",1000)
})
Any help is much appreciated
Thanks!
What Lars Kotthoff wrote would work. Alternatively – and I'm not sure which is more idiomatic:
Inside the click handler, the this context refers to the clicked DOM element.
So the following would do it too:
.on("click",function(d){
d3.select(this)
.transition()
.duration(1000)
.attr("r",1000)
});
You can use d3.event.target to access the element that is being clicked in the event handler. See for example this jsfiddle.