I'm trying to understand why in the following code I need Dragger.prototype.wrap and why I can't just use the event handling methods directly:
function Dragger(id) {
this.isMouseDown = false;
this.element = document.getElementById(id);
this.element.onmousedown = this.wrap(this, "mouseDown");
}
Dragger.prototype.wrap = function(obj, method) {
return function(event) {
obj[method](event);
}
}
Dragger.prototype.mouseDown = function(event) {
this.oldMoveHandler = document.body.onmousemove;
document.onmousemove = this.wrap(this, "mouseMove");
this.oldUpHandler = document.body.onmousemove;
document.onmouseup = this.wrap(this, "mouseUp");
this.oldX = event.clientX;
this.oldY = event.clientY;
this.isMouseDown = true;
}
Dragger.prototype.mouseMove = function(event) {
if (!this.isMouseDown) {
return;
}
this.element.style.left = (this.element.offsetLeft
+ (event.clientX - this.oldX)) + "px";
this.element.style.top = (this.element.offsetTop
+ (event.clientY - this.oldY)) + "px";
this.oldX = event.clientX;
this.oldY = event.clientY;
}
Dragger.prototype.mouseUp = function(event) {
this.isMouseDown = false;
document.onmousemove = this.oldMoveHandler;
document.onmouseup = this.oldUpHandler;
}
I'm told it's because this
changes without it, but I don't understand why this
changes, why the wrap function prevents it from changing, and what this
would change to without the wrap function.
You need to wrap them because when a function is used as an event handler, the
this
keyword refers to the DOM element that triggered the event, and if you don't wrap it, you don't have access to the instance members of yourDragger
object, likethis.isMouseDown
.For example:
Let's say you have a button:
And you have the following object:
If you call:
You will see an alert with the text contained in the
value
member of theobj
object ('I am an object member').If you use the
obj.method
function as an event handler:When the user clicks the button, it will alert 'Click me'.
Why? Because when the click event is fired,
obj.method
will be executed with thethis
keyword pointing to the DOM element, and it will alert 'Click me' because the button contains avalue
member.You can check the above snippets running here.
For context enforcement, I always keep close a bind function:
It allows you to wrap any function, enforcing the context. As the first argument, it receives the object that will be used as
this
, and the rest of optional arguments, are the ones the wrapped functionenter code here will be called with.In the button example we could use it as :
It's really helpful in a lot of situations and it will be introduced as part of ECMAScript 5.