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.
精彩评论