views:

319

answers:

3

First off, I know this is a base JS issue, not jQuery. I am at that learning point where I cannot seem to get my head completely wrapped around some scope issues. So I need some explanation.

I've been reading every article/tutorial I can find, but just need some answers. I setup this base code to experiment with (as the actual code is far too big to post here).

In the following experiment, when the selector1-change button is clicked the 'cmd' variable is displayed correctly, but the reference to this.options is 'undefined', because it is a different instance of MySelector(), unless I am way off.

So how does one go about calling an instance of an object like this.

Example:

You can use jQueryUI's dialog by creating it, and then later you can pass commands to it like $('#mydiv').dialog('close'); and that will access the instance attached to that element. How is that accomplished?

JQUERY_MYSELECTOR.JS

(function($){
    $.fn.MySelector = function(incoming) {

        if (typeof(incoming) != 'object') {
            return runCommand(incoming);
        }

        var options = $.extend({},{
            id: false,
            backgroundColor: false
        },incoming);

        function runCommand(cmd) {
            alert('blah:'+this.options.backgroundColor+"\n"+'cmd:'+cmd);
        }
    }
})(jQuery);

SCRIPT

<script type="text/javascript" src="jquery_myselector.js"></script>
<script type="text/javascript">
    $j(document).ready(function() {
        $j('#selector1).MySelector({ backgroundColor: '#000000' });
        $j('#selector1-change').click(function() {
            $j('#selector1').MySelector('mooocow');
        });
    });
</script>

STYLE

<input type="button" value="selector1" id="selector1" />
<br/>
<input type="button" value="selector1-change" id="selector1-change" />

UPDATED Forgot to use the plugin on selector1 (duh)

A: 

options was declared with the var statement in the your principal scope, you don't need to use the this keyword in your runCommand function, since in that function you have access to the outer closure:

    function runCommand(cmd) {
        alert('blah:'+options.backgroundColor+"\n"+'cmd:'+cmd);
    }
CMS
Already tried that. If you do not use 'this' Firebug throws a 'cannot access optimized closure' error.
Spot
+1  A: 

You could try something like this:

(function($){
    $.myPlugin = {
        foo: function () {...},
        bar: function () {...},
        foobar: function () {...}
    };

    $.fn.MySelector = function (operation) {
        if (typeof operation === 'string') {
            switch(operation) {
                'open':
                    $.myPlugin.foo();
                    break;
                'destroy':
                    $.myPlugin.bar();
                    break;
                default:
                    $.myPlugin.foobar();
                    break;
            }
        }
    }
}(jQuery));
RaYell
I am not looking for that. I want to be able to use it several times on the same page with different option stacks, but I also want to be able to pass commands to it. At this point, when I try to pass commands, it creates a new instance of the function.
Spot
You will have different options stack for each call because you are extending `options` with the passed `incoming` object. What kind of commands are you trying to pass that doesn't work as intended?
RaYell
The calling of runCommand is supposed to be after the plugin has been ran the first time. Much like when you run $('#myDiv').dialog(); (from jQueryUI) .. and then later you can run $('#myDiv').dialog('close'); After the plugin has been 'attached' to a DOM element, you can send commands to it. Many plugins accomplish this, but I cannot figure out how.
Spot
I've updated my code. Now it will perform different actions based on the first argument you pass.
RaYell
+1  A: 

You're calling runCommand before you assign a value to options - options has the value undefined inside the body of runCommand. Don't ask me how JS is able to see the body of runCommand before you declare it, but it can.

Also, at the first level of MySelector, this is bound to the results of running $j('#selector1-change'), which is an array. You will need to iterate that array.

Try something like this:

(function($){
    function runCommand(e, cmd) {
        alert('blah:'+ e.options.backgroundColor+"\n"+'cmd:'+cmd);
    }

    $.fn.MySelector = function(incoming) {
        this.each(function() {
            if (typeof(incoming) != 'object') {
                return runCommand(this, incoming);
            } else {
                this.options = $.extend(this.options || {
                    id: false,
                    backgroundColor: false
                },incoming);    
            }
        });
    }
})(jQuery);
Daniel Yankowsky
Ok I get what you are saying iterating the array. I will fix that. But the calling of runCommand is supposed to be _after_ the plugin has been ran the first time.Must like when you run $('#myDiv').dialog(); (from jQueryUI) .. and then later you can run $('#myDiv').dialog('close'); After the plugin has been 'attached' to a DOM element, you can send commands to it. Many plugins accomplish this, but I cannot figure out how.
Spot
I changed my code to behave in a more stateful fashion. Now I understand why you wanted to use `this.options`. When iterating with `each`, the callback function can access the current item with `this`. You can use that with `runCommand`, but you then also need to store the options on the DOM element, like this code now does.
Daniel Yankowsky
Thank you very much.
Spot