开发者

Authoring jQuery Plugin, add property to jQuery Object

I'm trying开发者_如何学C to write a plugin that creates a ul-list. Got it to working, but I can't figure out how to se if it's already loaded. I believe it's due to that it creates a new instance each time I call it. Is there a way around this.

I use this code for the plugin:

$.fn.addResultList = function (options) {
        var constructor = $('<ul></ul>'),
            offset = $(this).offsetParent();

        constructor.addClass(options.className);
        constructor.append('<li>Lorem ipsum</li>');
        constructor.css({
            backgroundColor : '#fff',
            left : offset.left + 'px',
            padding : '10px',
            position : 'absolute',
            top : offset.top + 'px',
            zIndex : '10'
        });

        $(this).after(constructor);

        modalObjects.push('.' + options.className);

        $('.' + options.className).hover(function() { 
            mouse_is_inside=true; 
        }, function() { 
            mouse_is_inside=false; 
        });


        this.isConstructed = true;

        return this;
    }

And call it like:

$('#related_project_name').addResultList({
                    className : 'related_result_list',
                    data : $.parseJSON(data),
                    callback : function () {

                    }
                });

But when I call it the second time it says that this.isConstructed is undefined.

Any ideas?

..fredrik


Since this is a jQuery plugin you should always return a jQuery extended object. This might sound unrelated, but it actually should fix your problem. Instead of returning this, return $(this).extend( { constructed: true } );. You'll get back the extended element with the constructed property.

If you're trying to make an object that you can treat like an object in a language like Ruby (eg, you want to be able to say someResultList.reload(); or similar), then you might want to look into John Resig's non-jQuery-dependent-but-complementary JavaScript Inheritance mini-library.

If you give more details on what exactly you're trying to do ('behavior-wise') then more help can appear :)


This is the solution I went with. Works great.

$.widget("ui.addResultList", {
        _init: function() {
            var constructor = $('<ul></ul>'),
                input = $('<input type="hidden" />'),
                offset = $(this.element).offsetParent();

            constructor.addClass(this.options.className);
            input.attr('id', this.options.className + '_inputid');
            input.attr('name', this.options.className + '_inputid');
            constructor.css({
                display: 'none',
                left : offset.left + 'px',
                position : 'absolute',
                top : offset.top + 'px',
                zIndex : '10'
            });

            this.constructor = constructor;
            this.keyListner = false;
            modalObjects.push(this.constructor);

            $(this.constructor).hover(function () { 
                mouse_is_inside = true; 
            }, function () { 
                mouse_is_inside = false; 
            });

            $(this.element).after(input);
            $(this.element).after(constructor);

        },

        _updateList : function (data) {
            var r = data.result,
                l = r.length,
                c = this.constructor,
                obj,
                i;

            c.html('');
            for (i = 0; i < l; (i += 1)){
                obj = r[i];
                if (i === 0) {
                    c.append('<li id="result_' + obj.id + '" class="selected"><a href="javascript:void(0)">' + obj.name + '</a></li>');
                } else {
                    c.append('<li id="result_' + obj.id + '"><a href="javascript:void(0)">' + obj.name + '</a></li>');
                }
            }
        },

        _addKeyEvents : function () {
            var _this = this,
                old,
                scrollY = 0;
            this.keyListner = true;
            this.element.keydown(function (e) {
                old = _this.constructor.find('.selected');
                scrollY = 42;
                if (_this.constructor.is(':visible') && (e.keyCode === 40 || e.keyCode === 38)) {
                    if (e.keyCode === 40) {
                        if (old.is(':last-child')) { return; }
                        old.next('li').addClass('selected');
                        scrollY += old.next('li').height();                     
                    } else if (e.keyCode === 38) {
                        if (old.is(':first-child')) { return; }
                        old.prev('li').addClass('selected');
                    }
                    old.prevAll().each(function (index, elem) {
                        scrollY += $(elem).height();
                    });

                    scrollY -= _this.constructor.height();
                    _this.constructor.get(0).scrollTop = scrollY;
                    old.removeClass('selected');
                }

                if (e.keyCode === 13) {
                    e.preventDefault();
                    if (e.stopPropagation) e.stopPropagation();
                    $('#' + _this.constructor.attr('class') + '_inputid').val(old.attr('id').split('_')[1]);
                    _this.element.val(old.find('a').html());
                    _this.close();
                    return false;
                }
            });

            this.constructor.find('li a').live('click', function () {
                $('#' + _this.constructor.attr('class') + '_inputid').val($(this.parentNode).attr('id').split('_')[1]);
                _this.element.val($(this).html());
                _this.close();
            });

            this.constructor.find('li a').live('mouseover', function () {
                _this.constructor.find('li').removeClass('selected');
                $(this.parentNode).addClass('selected');
            });
        },

        open : function () {
            this.constructor.get(0).scrollTop = 0;
            if (arguments[0].data.result.length) {
                this._updateList(arguments[0].data);
                if (!this.keyListner) {
                    this._addKeyEvents();
                }
                this.constructor.show();
            } else {
                this.close();
            }
        },

        close : function () {
            this.constructor.hide();
        },

        destroy : function() {
           $.widget.prototype.apply(this, arguments); // default destroy
        }
     });

     $.extend($.ui.addResultList, {
        getter: "value length",
        defaults: {
            className: 'related_result_list'
        }
     });

And and a new list with:

$('#related_project_name').addResultList();

And open it with:

$('#related_project_name').keyup(function (e) {
                if (e.keyCode === 13) {
                    e.preventDefault();
                    if (e.stopPropagation) e.stopPropagation();
                    return false;
                }

                if (e.keyCode !== 38 && e.keyCode !== 40) {
                    if ($(this).val().length > 1) {
                        model.jso.getConstructions($(this).val(), view.related.showProductList);
                    } else {
                        $('#related_project_name').addResultList('close');
                    }
                }
            });

Hope is help to anyone with the same problem. :)

..fredrik


don't know if it will work but give it a try:

var constructor;

if(! this.isConstructed){
    constructor = $('<ul></ul>');
}


To answer your original question, instead of saving state on the jQuery wrapper object you should use jQuery.data to attach data to a specific element:

$.fn.addResultList = function(options) {
  if (this.data("addResultList.isConstructed"))
    return this;

  // ... code here ...

  this.data("addResultList.isConstructed", true);
  return this;
};


Other way to add properties to a jQuery object is (its my own hack, so I'm not sure how popular or stable is, use it under your own risk): tested with jQuery 1.7 & 2.0b

$.fn.addResultList = function(options) {
    this.__proto__.constructed = true;
    return this;
}

Then you can use it like:

$("#myelem").addResultList();
if($("#myelem").constructed) ...

I have to say that in order to update again that value, you need to set it using the "__ proto __" property, otherwise it doesn't update the value. For example:

$("#myelem").constructed = false; //Will not update the value.

However, you can create a custom function to update it:

$.fn.addResultList = function(options) {
    this.__proto__.constructedValue = true;
    this.__proto__.isConstructed = function() {
         return this.constructedValue;
    }
    this.__proto__.constructed = function(newvalue) {
         this.__proto__.constructedValue = newvalue;
         return this;
    }
    return this;
}  

Be careful not to overwrite jQuery native methods/properties as your code may have unexpected behavior.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜