1

I'm writing a plugin for jQuery and I have a mess in my head with the context issue in javascript.

This is a simplified version of my plugin:

(function($){  

$.fn.myplugin = function(options){
    return new MyPlugin(this, options);
};


function MyPlugin(el, o )
{
    this.root    = el;
    this.input_box = el.find('#the_input');
    this.map = null;
    this.timeout = null;
    var self = this;

    self.input_box.keyup( function(){
    if( self.timeout!==null )
    {
       clearTimeout( self.timeout );
    }
    self.timeout = setTimeout( function(){
            self.search_address( self );
            }, 500 );
    });
}

MyPlugin.prototype = {

    search_address : function( context ){
          geocoder.geocode({'address':$('#direction').val()}, 
             function( results, status ){
              var lat = results[0].geometry.location.lat();
              var lng = results[0].geometry.location.lng();
              var latlng = new google.maps.LatLng( lat, lng );

              context.select_address( latlng, '' );                                   
    },

    select_address : function( latlng, text ){
        self.root.find('#url').val('http://maps.google.com/?q='+encodeURI($('#direccion').val())+'&ll='+coords.lat()+','+coords.lng()+'&ie=UTF8&oe=UTF8&z=18');
    }
};
})(jQuery);

I've read that setTimeout sets the context to the global object, so I did the closure to be able to pass the context to search_address function. This allowed me to call select_address from the callback in geocode function, but this callback is calling select_address and there the context is again undesired: self.root is not found.

I'm completly lost at how to handle the context, I though that using the var self=this at the initialization of the "object" I would avoid these problems...

I must note that select_address can be called directly...

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Pherrymason
  • 7,835
  • 8
  • 39
  • 57

2 Answers2

1

Use this at that point to refer to the current context:

select_address : function( latlng, text ){
    this.root.find('#url').val('http://maps.google.com/?q='+encodeURI($('#direccion').val())+'&ll='+coords.lat()+','+coords.lng()+'&ie=UTF8&oe=UTF8&z=18');
}
Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
  • why self doesn't work in there? Keep in mind that select_address can be called directly... I would like to know a rule to handle this and avoid trial and error. – Pherrymason Jan 03 '11 at 19:32
  • `self` is a variable you have, but it isn't present in the current scope. If you you need is the current context, you can use `this`, you just use a reference holder like `self` when `this` may change, like in a callback you don't control. – Nick Craver Jan 03 '11 at 19:35
  • So I should create a "self" var in any place the context may change and can not rely on the self created in the initializer... So it is a good practice having to pass a context to callbacks? – Pherrymason Jan 03 '11 at 19:39
  • 1
    @clinisbut - If you don't control them, yes that's a fine solution, especially in cases like `setTimeout()`, or create a closure to preserve the context. In your case there's no need to pass in `self`, you could instead of `self.search_address( self );` do `self.search_address.call(self);` and `this` would be `self` inside your `search_address` function...the first argument to `.call()` is the context :) – Nick Craver Jan 03 '11 at 19:43
1

I just found out something cool that fixes the SetTimeout context in jQuery 1.4+.

You can use the jQuery.proxy to solve the context issue.

Here is my code:


    Application = function()
    {
        this.AMethod = function(){};

        setTimeout($.proxy(this.AMethod,this), 100);


    }

After the timeout, the Application.AMethod is executed and thus the "Application" context is maintained

LE: Alternativelly you can create your own proxy function very simply if you don't want your plugin limited only to jQuery 1.4+ :


    function proxy(func, context)
    {
        return function(){ return func.apply(context, arguments);}
    }

AlexL
  • 1,699
  • 12
  • 20