0

I'm using jquery boilerplate template for my plugin. I need to deliver some callback from this plugin. This callback need to be some variable with offsets coordinates.

var coordinates = {
    x: x2, y: y2
};

I try to delegate this callback like this:

;(function ($, window, document) {

/* 'use strict'; */

// default options
var name = "plugin",
    defaults = {};

// constructor
function plugin (options, callback) {
    this.settings = $.extend({}, defaults, options);
    this.init();
    this.callback = callback;
}

plugin.prototype = {
    init: function () {
        var offset = $(this).offset(),
            x2 = (e.pageX - offset.left),
            y2 = (e.pageY - offset.top);

        $(document).on('mouseup', function() {
            var coordinates = {
                x: x2, y: y2
            };
            this.callback(coordinates);
        });
    }
};

// initialize
$.fn[name] = function (options, callback) {
    return this.each(function() {
        if (!$.data(this, "plugin_" + name)) {
            $.data(this, "plugin_" + name, new plugin(options, callback));
        }
    });
};

})(jQuery, window, document);

I have an arror that callback is not a method for this object. Can anybody help?

Lukas
  • 7,384
  • 20
  • 72
  • 127
  • Please provide more information on how you call your callback. – Kiruse Nov 20 '13 at 21:49
  • This is all what i have, i don't have idea how to do this... – Lukas Nov 20 '13 at 21:50
  • I don't believe you. Are you saying you replaced `function Plugin(){...}` with that itzy bitzy piece of code? If so, your call to your callback is *completely* out of place. Do you have a link to your code somewhere online? Like a Gist? – Kiruse Nov 20 '13 at 22:00
  • `this.callback(coordinates);` makes no since where it is located. – Kevin B Nov 20 '13 at 22:04
  • @Derija93 this is only a peace of code, check out jquery boilerplate model, i need delegate some offset coordinates to some variable, maybe callback is not a god idea... – Lukas Nov 20 '13 at 22:08
  • I did check it out. That's why I don't believe you. That's also why your call to your callback would be completely out of place, or in other words out of context. My guess is not that a callback won't work, but that you're using it wrongly. That's why I need to see HOW you're using it, that's why I'm asking for more code or a live demo... – Kiruse Nov 20 '13 at 22:15
  • see, now THAT makse sense. It isn't working there because `this` isn't what you think it is. – Kevin B Nov 20 '13 at 22:33

1 Answers1

2

Focus on how and especially where you call your callback:

plugin.prototype = {
    init: function () {
        var offset = $(this).offset(),
            x2 = (e.pageX - offset.left),
            y2 = (e.pageY - offset.top);

        $(document).on('mouseup', function() {
            var coordinates = {
                x: x2, y: y2
            };
            this.callback(coordinates);
        });
    }
};

You are creating an anonymous nested function. Anonymous functions by default have this === window.


Edit: Thanks to KevinB's comment, I noticed that my previous statement wasn't true for all cases, simply because it is possible to change the context of functions by calling .apply() and .call(), which jQuery does in order to allow you to simply use $(this) to access the element which triggered the event.

What I had in mind is that, if anonymous functions are called without these two methods, they, then this === window. But this is also true for methods which are called directly as functions, not as methods. The following won't work either.

var obj = { foo : 'foo', bar : function(){console.log(this.foo);} };
$(document).on('mouseup', obj.bar);

First of all because of the aforementioned change of context that jQuery does when calling the callback, 2nd because of a simple rule of thumb: the context is whatever is to the left of the dot. When calling a callback like this: callback(), nothing is to the left of the dot, i.e. this === null (don't hit me) which doesn't exist, so it defaults to this === window.


The fix to this is fairly easy: simply introduce a new variable that reference the instance of your plugin. This variable is commonly called that. Minor changes should accomplish your goal:

init: function() {
    var offset = $(this).offset(),
        x2 = (e.pageX - offset.left),
        y2 = (e.pageY - offset.top),
        that = this;

    $(document).on('mouseup', function(){
        var coordinates = {
            x: x2, y: y2
        };
        that.callback(coordinates);
    });
}

But beware: the way your plugin currently works, it will attach a listener to the mouseup event each time it is run. You don't need that many... especially since it will result in lags if you run the plugin a lot. I'd suggest to hook up the event listener once and call all callbacks one by one once the event has been triggered.

Kiruse
  • 1,703
  • 1
  • 12
  • 23
  • *"Anonymous functions always have this === window"* that is wrong, however your solution will fix it. in this case, `this` is `document`. – Kevin B Nov 20 '13 at 22:34
  • @KevinB That... is true. Because jQuery invokes the function by using `.call()`. I should add *by default*, thank you. – Kiruse Nov 20 '13 at 22:38
  • still i have this error: Object # has no method 'callback' – Lukas Nov 20 '13 at 22:42
  • @Lukas That shouldn't happen. Make sure you put `that = this` **not** inside your anonymous function as it wouldn't make a difference to what you had before. It **has to be** in the scope of `init`. Note that `HTMLDocument` is the "class" of `document`, which supports my guess. – Kiruse Nov 20 '13 at 22:48