javascriptjqueryarrayswidget

Array as JQuery Widget instance variable


To start - i don't have a full understanding of JQuery widgets. The way i see them are as objects that can hold a state and representation on their own. The examples i used for my design come from the microsoft 'SILK' project.

The problem - As i was busy creating visual elements as JQuery widgets for a new site everything went fine, untill i initialized more then 1 element with the widget. Instance variables (arrays) seemed global as opposed to everything else. So i created a simple test project to demonstrate the behaviour:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <script type="text/javascript" src="jquery-1.10.2.min.js"></script>
    <script type="text/javascript" src="jquery-ui-1.9.2.min.js"></script>
    <script type="text/javascript">
        (function ($, undefined) {
            $.widget('AR.MultiQueue', {
                options: {
                    queueName: "",
                    Names: [],
                    Wrong: [],
                    Right: []
                },

                _create: function () {
                    var temp = [];
                    for (var i = 0; i < this.options.Names.length; i++) {
                        console.log('pushing ' + this.options.Names[i] + ' for ' + this.options.queueName);
                        this.options.Wrong.push(this.options.Names[i]);
                        temp.push(this.options.Names[i]);
                    }

                    this.options.Right = temp;
                },

                Test: function () {
                    console.log(this.options.queueName);
                    for (var i = 0; i < this.options.Names.length; i++)
                        console.log('Name->' + this.options.Names[i]);
                    for (var i = 0; i < this.options.Wrong.length; i++)
                        console.log('Wrong->' + this.options.Wrong[i]);
                    for (var i = 0; i < this.options.Right.length; i++)
                        console.log('Right->' + this.options.Right[i]);
                },

                destroy: function () {
                    $.Widget.prototype.destroy.apply(this, arguments);
                }
            });
        }(jQuery));

    </script>
</head>
<body>
<div id="a"></div>
    <div id="b"></div>
    <script type="text/javascript">
        (function () {
            $('#a').MultiQueue({ queueName: 'a', Names: ["a"] });
            $('#b').MultiQueue({ queueName: 'b', Names: ["b"] });
            setTimeout(function () {
                $('#a').MultiQueue('Test');
                console.log('Next widget');
                $('#b').MultiQueue('Test');
            }, 1000);
        })();


    </script>
</body>
</html>

So the output I expected is for each widget to only print out the respective ("a" or "b") array string 3 times. Instead the output is:

pushing a for a
pushing b for b
a
Name->a
Wrong->a
Wrong->b
Right->a
Next widget
b
Name->b
Wrong->a
Wrong->b
Right->b

As you can see, both the widgets print out "a" AND "b" for the array that uses a direct instance variable 'push'. For now im just rewriting my widgets to use a temporary array object like the 'Right' options var in the example, but i get the feeling there is more to jQuery widgets then i can understand (as in, do i write code the way i should???)

I searched the web for this, but the only thing i can come up with is
- dont use jQuery PLUGINS as instances
- something with jQuery widgets being stored in arrays
- the fact i never really understood arrays in javascript (as the fact arrays have fixed sizes and the closest is a List<> in C#).

Anyway, i really want to know why this is happening (and if i could expect more of these suprises, this took a bit of time to find out)

Thanks


Solution

  • Use null instead of [] inside the options and only start them on _create or _init. This happens because the array is instantiated once for that widget type (basically speaking) and kept as a reference for every new widget created. Primitive types are copied (recursively) but arrays are referenced so when jQuery copy the options the array stays the same.