destroying jquery ui plugin instances

2.1k views Asked by At

I have a tagging plugin.

The plugin is getting used on an element of a form that is generated via ajax.

something like

...
success : function(data) {
   $.lightbox(data);
   $('#groups').tagit();
}
...

Now when the form is submitted (by ajax) the lightbox is removed, hence so is the form.

But,

If I click the button to create a new form the plugin is in its previous state, even though #groups is now a completely new dom element.

why is this and how can I fix it?

(by in the same state I mean)

I have a variable is the plugin

_vars : {tags: []}

this gets populated as tags are added. when the plugin is called again on the new #groups the tags variable contains all tags from the previous instance.

how can I fix this?


Code

$.widget("ui.tagit", {

    // default options
    options: {
        tagSource:   [],
        allowSpace:  true,
        initialTags: [],
        minLength:   1
    },

    //private variables
    _vars: {
        lastKey: null,
        element: null,
        input:   null,
        tags:    []
    },

    _keys: {
        backspace: 8,
        enter:     13,
        space:     32,
        comma:     44
    },

    //initialization function
    _create: function() {

        var instance = this;

        //store reference to the ul
        this._vars.element = this.element;

        //add class "tagit" for theming
        this._vars.element.addClass("tagit");

        //add any initial tags added through html to the array
        this._vars.element.children('li').each(function() {
            instance.options.initialTags.push($(this).text());
        });

        //add the html input
        this._vars.element.html('<li class="tagit-new"><input class="tagit-input" type="text" /></li>');

        this._vars.input = this._vars.element.find(".tagit-input");

        //setup click handler
        $(this._vars.element).click(function(e) {
            if (e.target.tagName == 'A') {
                // Removes a tag when the little 'x' is clicked.
                $(e.target).parent().remove();
                instance._popTag();
            }
            else {
                instance._vars.input.focus();
            }
        });

        //setup autcomplete handler

        this.options.appendTo = this._vars.element;
        this.options.source = this.options.tagSource;
        this.options.select = function(event, ui) {
            instance._addTag(ui.item.value, ui.item.id);
            return false;
        }
        this._vars.input.autocomplete(this.options);

        //setup keydown handler
        this._vars.input.keydown(function(event) {
            var lastLi = instance._vars.element.children(".tagit-choice:last");
            if (event.which == instance._keys.backspace)
                return instance._backspace(lastLi);

            if (lastLi.hasClass('selected'))
                lastLi.removeClass('selected');

            // Comma/Space/Enter are all valid delimiters for new tags.
            else if (event.which == instance._keys.comma || (event.which == instance._keys.space && !instance.options.allowSpace) || event.which == instance._keys.enter) {
                event.preventDefault();
                instance._addTag(this.value, 0);
            }
            instance._vars.lastKey = event.which;
        })

        //define missing trim function for strings
        String.prototype.trim = function() {
            return this.replace(/^\s+|\s+$/g, "");
        };

        this._initialTags();

    },
    _popTag: function() {
        return this._vars.tags.pop();
    },

    _addTag: function(value, id) {
        id = (id == null ? 0 : id);
        this._vars.input.val("");
        value = value.replace(/,+$/, "");
        value = value.trim();
        if (value == "" || this._exists(value))
            return false;

        var tag = "";
        tag = '<li class="tagit-choice">' + value + '<a class="tagit-close">x</a></li>';
        $(tag).insertBefore(this._vars.input.parent());
        this._vars.input.val("");
        this._vars.tags.push({id: id, value: value});
    },

    _exists: function(value) {
        if (this._vars.tags.length == 0)
            return false;
        for (var i = 0; i <= this._vars.tags.length-1; i++)
            if (this._vars.tags[i].value == value)
                return true;
        return false;
    }
    ,

    _oc: function(array) {
        var object = {};
        for (var i = 0; i < array.length; i++) {
            object[array[i]] = '';
        }
        return object;
    }
    ,

    _backspace: function(li) {
        if (this._vars.input.val() == "") {
            // When backspace is pressed, the last tag is deleted.
            if (this._vars.lastKey == this._keys.backspace) {
                $(this)._tagger('remove');
                li.remove();
                this._vars.lastKey = null;
            } else {
                li.addClass('selected');
                this._vars.lastKey = this._keys.backspace;
            }
        }
        return true;
    }
    ,

    _initialTags: function() {
        if (this.options.initialTags.length != 0) {
            for (var i in this.options.initialTags)
                if (!this._exists(this.options.initialTags[i]))
                    this._addTag(this.options.initialTags[i]);
        }
    }
    ,

    tags: function() {
        return this._vars.tags;
    }
})
        ;
1

There are 1 answers

2
Luke On BEST ANSWER

you could do following

success : function(data) {
   $.lightbox(data);
   $('#groups').tagit("destroy").tagit();
}

dont forget to override the destroy method in your class

destroy: function() {
       $.Widget.prototype.destroy.apply(this, arguments); // default destroy
        // now do other stuff particular to this widget
   }

for your need, i think you should destroy your vars, clear them, free connections and unbind events. the destroy method should restore the element on which the widget was applied to the original state.

This for example is doing the base destroy method, just to know, what for you can use it.

destroy: function() {
        this.element
            .unbind( "." + this.widgetName )
            .removeData( this.widgetName );
        this.widget()
            .unbind( "." + this.widgetName )
            .removeAttr( "aria-disabled" )
            .removeClass(
                this.widgetBaseClass + "-disabled " +
                "ui-state-disabled" );
    }