I am getting an undefined symbol inside of a prototype function. I write to console.log the ‘this’ and ‘self’ values. The values change from the first invocation via a direct call to later calls via the callback from ‘zoom’. I have a jsfiddle, http://jsfiddle.net/eric_l/kQSTH/, and opening the console will allow one to see the error messages. Any idea from neither ‘this’ nor ‘self’ are working correctly?
<!DOCTYPE html>
<meta charset="utf-8">
<title>Zoom by Rectangle</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
body {
font-family: sans-serif;
}
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
rect {
fill: #ddd;
}
.axis path, .axis line {
fill: none;
stroke: #fff;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 3.0px;
}
</style>
<p><label for="zoom-rect"><input type="checkbox" id="zoom-rect"> zoom by rectangle</label>
<table><tr><td> <div id="chart1"> </div> </td></tr></table>
<script>
var zoomRect = false;
d3.select("#zoom-rect").on("change", function() {
zoomRect = this.checked;
});
var Chart = function( _width, _height, _anchor )
{
var self = this;
this.anchor = _anchor;
this.margin = {top: 0, right: 12, bottom: 12, left: 36},
this.width = _width - this.margin.left - this.margin.right,
this.height = _height - this.margin.top - this.margin.bottom;
this.data = [ {x:0 , y:0},
{x:this.width*0.25, y:this.height},
{x:this.width*0.5 , y:this.height/2},
{x:this.width*0.75, y:this.height},
{x:this.width , y:0} ];
this.xscale = d3.scale.linear()
.domain([0, this.width])
.range([0, this.width]);
this.yscale = d3.scale.linear()
.domain([0, this.height])
.range([this.height, 0]);
this.xAxis = d3.svg.axis()
.scale(this.xscale)
.orient("bottom")
.tickSize(-this.height);
this.yAxis = d3.svg.axis()
.scale(this.yscale)
.orient("left")
.ticks(5)
.tickSize(-this.width);
this.zoom = d3.behavior.zoom().x(this.xscale).y(this.yscale).on("zoom", this.refresh);
this.svg = d3.select(this.anchor).append("svg")
.attr("width", this.width + this.margin.left + this.margin.right)
.attr("height", this.height + this.margin.top + this.margin.bottom)
.append("g")
.attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")")
.call(this.zoom)
.append("g")
.on("mousedown", function() {
if (zoomRect)
{
d3.event.stopPropagation();
}
})
.on("mousemove", function() {
if (zoomRect)
{
d3.event.stopPropagation();
}
});
this.svg.append("rect")
.attr("width", this.width)
.attr("height", this.height);
this.line = d3.svg.line()
.x(function (d) { return self.xscale(d.x) } )
.y(function (d) { return self.yscale(d.y) } )
this.path = this.svg.append("path")
.attr("class", "line")
.datum(this.data)
.attr("d", this.line);
this.svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + this.height + ")")
.call(this.xAxis);
this.svg.append("g")
.attr("class", "y axis")
.call(this.yAxis);
}; // Chart
Chart.prototype.refresh = function() {
console.log("this=" + this);
console.log("self=" + self);
console.log("this.svg=" + this.svg);
console.log("self.svg=" + self.svg);
self.svg.select("path").attr("d", self.line );
self.svg.select(".x.axis").call(self.xAxis);
self.svg.select(".y.axis").call(self.yAxis);
}
var charts = [];
charts.push( new Chart( 760, 400, "#chart1"));
for( var i = 0; i < charts.length; ++i ) {
charts[ i ].refresh();
}
</script>
Thank you.
thisis a reserved keyword,selfis not. Self is commonly used to close in the value of this within a function that may not be called on the object itself. However, doing aconsole.log(this)inside your refresh function reveals it is indeed aChartobject.Use
thisinside that function, not self; alternatively execute this line (though it’s redundant)EDIT: while self is not a reserved word,
window.selfis a global property. Hence whyselfby itself, with no other contexts, will bewindowEDIT 2:
this line will cause the errors as stated
In particular, it’s the binding of the zoom event. From the wiki, the function will have
(emphasis mine).
you need to proxy the event through your Chart object, so change the binding to something like this:
updated js fiddle