How to invoke "click" event programmatically in d3? - javascript

I'm trying like that (also at https://gist.github.com/1703994):
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.27.2"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.time.js?1.27.2"></script>
<script type="text/javascript" src="js-libs/jquery-1.7.js"></script>
<style>
<!--
#test {
width: 400px;
height: 500px;
}
-->
</style>
</head>
<body>
<script type="text/javascript">
$(function() {
var w = 600,
h = 350;
var vis = d3.select("#test").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
var g = vis.selectAll("g")
.data([ { x:1 , y: 2} ])
.enter().append("svg:g");
g.append("svg:path")
.attr("fill", "red")
.attr("stroke", "red")
.attr("stroke-width", "10")
.attr("d", "M 100 350 l 150 -300")
g.select("path")
.on("click", function() { console.log("Hello"); });
// XXX: how to execute click programmaticaly?
})
</script>
<div id="test"></div>
</body>
</html>
But doesn't work
I think we may use https://github.com/mbostock/d3/wiki/Internals#wiki-dispatch_on
But how to do it?

not sure why, but there appears to be a discrepancy with the way jQuery and d3 handle events that causes a jQuery induced click event $("#some-d3-element").click() to not dispatch to the d3 element.
a workaround:
jQuery.fn.d3Click = function () {
this.each(function (i, e) {
var evt = new MouseEvent("click");
e.dispatchEvent(evt);
});
};
and then call it:
$("#some-d3-element").d3Click();

Simply call the .on method as a getter for the registered value (i.e. your handler function), then call the result of that:
g.select("path").on("click")();
It gets a little more complicated if your handler uses the bound data and/or event fields, or if you've got multiple event listeners bound (e.g "click.thing1" and "click.thing2"). In that case, you're probably best off just firing a fake event using the standard DOM methods:
var e = document.createEvent('UIEvents');
e.initUIEvent('click', true, true, /* ... */);
g.select("path").node().dispatchEvent(e);

With D3 v4 you will likely want this:
d3.select('#some-id').dispatch('click');
Ref.: https://github.com/d3/d3-selection#selection_dispatch

This works. I'm using pie charts, so I'm selecting all the "selected" pie slices, and for each of them, retrieving the attached "click" callback (that I have attached in another portion of code not included here, using d3's .on() method) and then invoking with the expected parameters in the correct context.
d3.selectAll("g.selected").each(function(d, i) {
var onClickFunc = d3.select(this).on("click");
onClickFunc.apply(this, [d, i]);
});

This answer might be somewhat unrelated - but hopefully useful to someone searching for how to invoke a click event of a SVG element - since jQuery $(mySvgElement).trigger("click") won't work.
This is how you would programmatically trigger/invoke/raise a click event for a SVG element:
var ev = document.createEvent("SVGEvents");
ev.initEvent("click",true,true);
var target = $("svg>g>path[fill='#0011cc']").get(0); // get the SVG element here
target.dispatchEvent(ev); // like $(target).trigger('click') - but working!

I came this thread looking for a d3 mousemove event for angular unit testing.
#natevw answer
g.select("path").on("click")();
helped a lot on mouseover event. But, applying that to mousemove was giving an e.source null error.
The work around was to set the d3 event programmatically.
d3.event = document.createEvent('MouseEvent');
d3.event.initMouseEvent("mousemove");
d3.select(elm[0]).select("rect").on("mousemove")();
Hope this helps.

You can go super manual by getting the mouse event and passing it the arguments that d3 would otherwise provide for you. This gives you a fairly clean way to do it while still using d3 constructs. For a single element use the following:
var path = g.select('path');
path.on('click').call(path.node(), path.datum());
For multiple elements, you can trigger each one in turn:
g.selectAll('path').each(function(d, i) {
d3.select(this).on('click').apply(this, arguments);
});
The latter can also be used for a single element if your selector is specific enough, or if you use .select() instead of .selectAll() to only return the first element.

#handler answer did not work for me entirely. It would click the svg element but additional simulated events would not register. This is what worked for me:
function eventFire(el, etype){
if (el.fireEvent) {
el.fireEvent('on' + etype);
} else {
var evObj = document.createEvent('Events');
evObj.initEvent(etype, true, false);
el.dispatchEvent(evObj);
}
}
Usage:
eventFire(document.getElementById(element_id), 'click');

This is how I do it.
g.selectAll("path").on("click", function(d, i){
my_function(d, i);
});
I've found the the callbacks work with anonymous functions. So for the code above, any path that is clicked will call my_function and pass in the current datum d and index i of the path that was clicked.

I find next workaround:
d3.selectAll("path").each(function(d, i) {
onClickFunc.apply(this, [d, i]);
});
Where d is data and i is index this data

Try g.select("path").trigger("click")

Related

Snap.svg: How to use Set.bind(...)?

I can't figure out how to use the Set.bind(...) feature for Snap.svg.
Below is an example with three(3) elements in a set: 2 circles and an ellipse.
I'd like to access and change some attr's in the various elements, using bind.
A few examples of bind would be appreciated.
(Actually, at this moment, I can't see any advantage in using the Set object, rather than an array. Are there any features of the Set that can't be handled just as well with an array?)
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="http://svgDiscovery.com/_SNP/snap.svg-min.js"></script>
</head>
<body>
<svg id=mySVG width=400 height=200></svg>
<script>
var SNPsvg = Snap("#mySVG");
var circle1 = SNPsvg.circle(150,100,50).attr({fill: 'red' });
var circle2 = SNPsvg.circle(250,100,50).attr({fill: 'blue' });
var ellipse = SNPsvg.ellipse(200,100,50,20).attr({fill: 'green' });
var mySet= Snap.set(circle1,circle2,ellipse)
setTimeout(function()
{
//mySet.bind(...)
},1000)
</script>
</body>
</html>
The main reason to use a set, is that you can act on every element with a single command. For example...
mySet.animate({ transform: 's2' },1000)
jsfiddle
Which will then act on every single element with that animation.
Why would you use Set.bind ? I must admit, I've never used it, nor seen the purpose yet, but I assume there is one :).
So to the actual question, how is it used. I guess you do..
mySet.bind('x', circle1, 'cx' )
mySet.attr({ 'x': '200' })
jsfiddle
If you set attribute x, it will set attribute cx on circle1 in this case.
Or
mySet.bind('x', function( val ) { console.log( val, ' is passed' )} )
mySet.attr({ 'x': '200' })
jsfiddle
As to why though, I'm not sure :), I can see the advantage of using a set object, but not particularly with set.bind(), especially as it doesn't seem to pass 'this' to the function. I was wondering if it was something like if you set x on a set of circles and rects, you could adjust cx OR x somehow, but I don't see how that is done in any simple way, if the object that's being acted on isn't passed somehow.
I'd normally be more inclined to do something like...
mySet.forEach( function( el ) { el.attr({ fill: 'blue' }) } );

Selecting SVG with D3

I have the following function:
function myfunc(d) {
var svg = d3.select('.map-wrap svg');
console.log('svg is:');
console.log(svg);
// interesting stuff happens later ...
}
I call that function on a mouseover event. Here is code where myfunc is supposed to be called.
myChart.width(800)
.height(800)
.dimension(xDim)
.group(xDimGrp)
.projection(projection)
.colors(quantize)
.colorDomain(quantize.domain())
.colorCalculator(function (d) { return d ? getColorClass(myChart.colors()(d)) : '#ccc'; })
.overlayGeoJson(map.features, 'states', function (d) {
return d.properties.state;
}).on('mouseover', myfunc);
When I print out svg I expect to see this:
instead, I see the following:
I see 0: null instead of 0: svg, why is this happening? How can I select the SVG in a way that will give me what is shown in first picture?
.map-wrap is like so:
It was as Cyril said, after debugging better, I realised that it was indeed being called before the creation of the svg element.

How to override the dragging events in C3.js

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

How can I add a transition effect to a radius increase on a circle?

I have the following code that allows me to increase the size of a circle when it is clicked. I want to add a transition effect, but it isn't working:
D3.js
d3.selectAll(".bubble-node")
.on("click", function (d) {
$("#circle-" + d.id).transition().duration(1000).attr("r", r);
d.forceR = r; // forceR is a property on the data object
});
HTML/svg
<a class="bubble-node" id="bubble-id-3" style="fill: #62d5f4">
<circle id="circle-8" r="65"></circle>
</a>
What am I missing?
not sure exactly you're trying to do but i think the selector is wrong for d.id
try using this.id
$(".bubble-node")
.on("click", function (d) {
$("#circle-" + this.id).transition().duration(1000).attr("r", r);
d.forceR = r; // forceR is a property on the data object
});
but then the id would not match up when you concatenate "#circle-bubble-id-3" doesn't exist in the DOM. So you might want to change the id of the A element to "8" then it would concatenate to "#circle-8" effectively selecting your circle object.
Also if you're using jQuery I would stick with animate() unless transition() is a plugin you're using.

Making charts clickable

I am just learning and trying to code it at the same time, this is the code I have, If I take out the drill method and take out the .click(drill) then everything works so far, it draws some silly bar charts from the data I am sending to it
$( document ).ready(function() {
var dataset = gon.data;
d3.select("body").selectAll("div")
.data(dataset)
.enter()
.append("div")
.attr("class", "bar")
.click(drill)
.style("height", function(d) {
return d.brand_name + "px";
});
function drill (event) {
var target = event.currentTarget;
var data = $(target).data();
console.log(data);
}
});
But I am not able to add "click" event to those bar charts such that when I click on them I can know which chart I clicked on. The code above is result of my unsuccessful attempt to add "click" event to the charts I have drawn .... What is the correct way?
You can use .on("click", drill) if you want D3 to pass the object and data attached.
function drill(d, i) {
console.log(d); //data object
console.log(i); //array position
console.log(this); //DOM element
}
You can also use .attr("onclick", "drill()") syntax if you want to follow the standard HTML without the D3 wrapper.

Categories

Resources