I was busting my head on this problem for some time...
I have a usual constructor/prototype object (like a class) in js which holds all of my d3 chart logic:
figureGen = function(html_element) {
this.svg = d3.select(html_element)
.append('svg')
.style('width', '100%')
.style('height', '100%')
.append('g')
.attr("class", "sadrzalac")
.attr("transform", "translate(" + 0 + "," + 0 + ")");
this.element = element;
this.data = [];
this.yRange = d3.scale.linear();
this.xRange = d3.scale.linear();
this.xAxis = undefined;
this.yAxis = undefined;
this.width = undefined;
this.height = undefined;
this.zoom = undefined;
this.margin = {top: 20, right: 20, bottom: 20, left: 35};
this.svg.append("rect")
.attr("class", "clipPath")
.attr("transform", "translate(" + this.margin.left + "," + -this.margin.top + ")");
this.svg.append('g')
.attr("class","xaxis axis");
this.svg.append('g')
.attr("class", "yaxis axis");
}
for the constructor, and for the methods something like this
figureGen.prototype = {
constructor: figureGen,
init: function(sampledata, width, height){
var y_min = Number(d3.min(this.podaci, function (d) { return d.y;}));
var y_max = Number(d3.max(this.podaci, function (d) { return d.y;}));
var x_min = Number(d3.min(this.podaci, function (d) { return d.x;}));
var x_max = Number(d3.max(this.podaci, function (d) { return d.x;}));
var ukupan_opseg = y_max - y_min;
var spacer = (ukupan_opseg*25)/100;
this.xRange = d3.scale.linear().range([this.margin.left + 10, this.width - this.margin.right]).domain([x_min, x_max]);
this.yRange = d3.scale.linear().range([this.height - this.margin.top, this.margin.bottom]).domain([y_min-spacer, y_max+spacer]);
this.zoom = d3.behavior.zoom()
.x(this.xRange)
.y(this.yRange)
.scaleExtent([1, 5])
.center([this.width / 2, this.height / 2])
.size([this.width, this.height])
.on("zoom", this.zoomProcess());
this.svg
.attr("width", this.sirina)
.attr("height", this.visina)
.call(this.zoom);
this.xAxis = d3.svg.axis()
.scale(this.xRange)
.innerTickSize(-this.height+40)
.outerTickSize(5)
.tickPadding(10)
.tickFormat(d3.format("d"));
this.yAxis = d3.svg.axis()
.scale(this.yRange)
.orient("left")
.innerTickSize(-this.width)
.outerTickSize(5)
.tickPadding(10)
.tickSubdivide(false);
this.svg.select('.xosa')
.attr("transform", "translate(0," + (this.height - this.margin.bottom) + ")")
.call(this.xAxis);
this.svg.select(".yosa")
.attr("transform", "translate(" + (this.margin.left) + ",0)")
.call(this.yAxis);
},
zoomProcess: function{
this.svg.select(".xaxis").call(this.xAxis);
this.svg.select(".yaxis").call(this.yAxis);
console.log(this.svg);
}
}
and the problem is in the zoomProcess method which always returns (logs) HTML element on the this.svg instead of this.svg object which can .select. Zoom behaviour is properly attached to my element, and on mouse whell the zooming starts but it pops up an error saying that this .call on this.xAxis can't be performed.
What's even stranger when I trigger zoomProcess from another source, like a button click its console log outputs the proper this.svg object and not the HTML element, so it seems that the error comes from the zoom event handler. What's wrong here?
These are the two examples I've been looking at:
http://bl.ocks.org/stepheneb/1182434
http://bl.ocks.org/mbostock/7ec977c95910dd026812
EDIT: Now I guess that I probably should use .bind() on the this object so that its reference stays the same when going to zoomProcedura method. Still I`m not sure how to do this.
Selection methods come in two forms: select and selectAll: the former selects only the first matching element, while the latter selects all matching elements in document order. The top-level selection methods, d3.
I assume there is a typo that is not in your actual code...
zoomProcess: function{
this.svg.select(".xaxis").call(this.xAxis);
this.svg.select(".yaxis").call(this.yAxis);
console.log(this.svg);
}
is probably...
zoomProcess: function(){
this.svg.select(".xaxis").call(this.xAxis);
this.svg.select(".yaxis").call(this.yAxis);
console.log(this.svg);
}
or maybe...
zoomProcess: function(){
return function() {
this.svg.select(".xaxis").call(this.xAxis);
this.svg.select(".yaxis").call(this.yAxis);
console.log(this.svg);
}
}
I can't know for certain. And I assume you really are invoking like...
.on("zoom", this.zoomProcess());
Not...
.on("zoom", this.zoomProcess);
But anyway, based on that assumption, to make it work the way you want, try...
zoomProcess: function(){
//'this' is figureGen.prototype
var that = this;
return function () {
//'this' is window
that.svg.select(".xaxis").call(that.xAxis);
that.svg.select(".yaxis").call(that.yAxis);
console.log(that.svg);
}
}
or...
zoomProcess: function(){
//'this' is figureGen.prototype
return function () {
//'this' is figureGen.prototype
this.svg.select(".xaxis").call(this.xAxis);
this.svg.select(".yaxis").call(this.yAxis);
console.log(this.svg);
}.bind(this)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With