Scope of variables in Javascript
I'm having trouble understanding the scope of 'this' in this scenario. I'm able to call each one of these functions like: this.startTracking(); from within the time tracker switch object. However, when I try to execute the code: Drupal.timeTracker.prototype.stopTracking(); It loses all scope of variables and my GET request becomes undefined. How can I fire off stopTracking() onbeforeunload?
Drupal.behaviors.dynamicTimeTracker = function (context) {
$('form.node-time-tracker', context).each(function () {
new Drupal.timeTracker(this);
});
};
/**
* A time tracker switch object
*/
Drupal.timeTracker = function (form) {
var tracker = this;
this.form = form;
this.nid = $('#'+ form.id +' input[name="nid"]').attr('value');
this.uid = $('#'+ form.id +' input[name="uid"]').attr('value');
this.button = $('#'+ form.id +' input[type="submit"]');
this.url = Drupal.settings.time_tracker.url + '/' + this.nid + '/' + this.uid;
this.counter = $('#'+ form.id +' .counter');
this.initialize(); // TODO: make sure this function is called regularly to make sure trackers are in synch
this.startTracking();
$(window).bind('beforeunload', function() {
Drupal.timeTracker.prototype.stopTracking(); // need help here
});
};
/**
* Initialize the time tracker
*/
Drupal.timeTracker.prototype.initialize = function () {
var tracker = this;
$.ajax({
type: "GET",
url: tracker.url,
dataType: 'json',
success: function (status) {
$(tracker.counter).countdown({compact:true, since:-status['time']}).countdown('resume');
if (status['status'] == 'ongoing') {
$(tracker.button).toggle(
function() {
tracker.stopTracking();
return false;
},
function() {
tracker.startTr开发者_Go百科acking();
return false;
}
);
$(tracker.counter).countdown('resume');
$(tracker.button).val(Drupal.t('Stop'));
$(tracker.form).removeClass('node-time-tracker-start').addClass('node-time-tracker-stop');
}
else {
$(tracker.button).toggle(
function() {
tracker.startTracking();
return false;
},
function() {
tracker.stopTracking();
return false;
}
);
$(tracker.counter).countdown('pause');
$(tracker.button).val(Drupal.t('Start'));
$(tracker.form).removeClass('node-time-tracker-stop').addClass('node-time-tracker-start');
}
},
error: function (xmlhttp) {
alert(Drupal.ahahError(xmlhttp, tracker.startURL));
}
});
};
/**
* Starts time tracking
*/
Drupal.timeTracker.prototype.startTracking = function () {
var tracker = this;
// Ajax GET request for starting time tracker
$.ajax({
type: "GET",
url: tracker.url + '/start',
dataType: 'json',
success: function (status) {
$(tracker.counter).countdown('change', {since: -status['time']}).countdown('resume');
$(tracker.button).val(Drupal.t('Stop'));
$(tracker.form).removeClass('node-time-tracker-start').addClass('node-time-tracker-stop');
},
error: function (xmlhttp) {
alert(Drupal.ahahError(xmlhttp, tracker.startURL));
}
});
};
/**
* Stops time tracking
*/
Drupal.timeTracker.prototype.stopTracking = function () {
var tracker = this;
// Ajax GET request for stopping time tracker
$.ajax({
type: "GET",
url: tracker.url + '/stop',
dataType: 'json',
success: function (status) {
$(tracker.counter).countdown('change', {since: -status['time']}).countdown('pause');
$(tracker.button).val(Drupal.t('Start'));
$(tracker.form).removeClass('node-time-tracker-stop').addClass('node-time-tracker-start');
},
error: function (xmlhttp) {
alert(Drupal.ahahError(xmlhttp, tracker.startURL));
}
});
};
I'll just take out a little snippet:
this.startTracking();
$(window).bind('beforeunload', function() {
// this is defined by the above function definition
tracker.stopTracking();
});
Your problem is, that when you create the bind function, this
inside it, will refer to $(window)
, so you need to create a copy of this, to be able to reference it inside this new function.
googletorp's answer should work, but just a quick note:
I believe the issue you're having is because you're trying to call:
Drupal.timeTracker.prototype.stopTracking();
Instead, I think it should it be:
Drupal.timeTracker.stopTracking();
I don't believe you're supposed to call functions on the prototype, but rather on the object whose prototype you've modified.
When you call new Drupal.timeTracker(this)
, it creates a new object. That object inherits from Drupal.timeTracker.prototype
. Inside the methods of that object, this
is set to the object itself. Drupal.timeTracker.prototype
is not the object, but merely a template from which new instances of the object may be formed; Like a cookie-cutter, it's not very good to eat. Most importantly, none of the inner state of your actual timer lives there.
When you call Drupal.timeTracker.prototype.stopTracking
, it's being called on the template, not the real object. If you call tracker.stopTracking()
, you'll be okay. You can't call this.stopTracking()
, because when you want to call stopTracking
, you're in the event handler for onbeforeunload
, so your this
will be the window
object.
精彩评论