jquery autocomplete && linkage
30513 ワード
ネット上の既存のcomboboxを参考にして、改造と完備を経て、特に分かち合います.
次の機能をサポートします.
1.ドロップダウン・エントリのカスタム表示
2.ロード中の友好的なメッセージ
3.linkageと連携し、構造連動(国/地域)を容易にする
足りないところ、
1.jquery positionプラグインを使用してドロップダウン位置を位置決めし、自身に欠陥があることを実現する
2.勤務会社はURLパラメータのカスタマイズが重すぎて、JSコードはpostdata検査を埋め込む.linakgeキャッシュアルゴリズムが悪すぎます
次の機能をサポートします.
1.ドロップダウン・エントリのカスタム表示
2.ロード中の友好的なメッセージ
3.linkageと連携し、構造連動(国/地域)を容易にする
足りないところ、
1.jquery positionプラグインを使用してドロップダウン位置を位置決めし、自身に欠陥があることを実現する
2.勤務会社はURLパラメータのカスタマイズが重すぎて、JSコードはpostdata検査を埋め込む.linakgeキャッシュアルゴリズムが悪すぎます
.c-list {
overflow:auto;
position: absolute;
z-index: 999999;
display: none;
border: 1px solid #CCCCCC;
background-color: white;
left:-9999px;
top:-9999px;
}
.c-list .item {
display:block;
overflow: hidden;
word-break: normal;
word-wrap: break-word;
cursor: pointer;
padding: 2px 0px 2px 5px;
border-bottom: 1px dashed #CCCCCC;
}
.c-list .item-selected {
background: #c8e3fc;
}
;(function($) {
$.i18n=$.i18n || {};
$.i18n.autocomplete={
NO_DATA:' ',
LOAD_ERROR:' ',
LOAD_TEXT:' ...'
};
if (!$.browser) {
var userAgent = navigator.userAgent.toLowerCase();
// Figure out what browser is being used
$.browser = {
version : (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
safari : /webkit/.test(userAgent),
opera : /opera/.test(userAgent),
chrome: /chrome/.test(userAgent),
msie : /msie/.test(userAgent) && !/opera/.test(userAgent),
mozilla : /mozilla/.test(userAgent)
&& !/(compatible|webkit)/.test(userAgent)
};
}
/** **/
var ele = document.createElement("input");
var _support_placeholder = 'placeholder' in ele;
ele = null;
/**
* autocomplete
* @constructor
*/
$.autocomplete = function($element, options) {
var self = this,input = $element[0],name=options.name || input.name;
this.$element = (options.cls ? $element : $element.addClass(options.cls)).removeAttr("name");
input.autocomplete = 'off';
input.value='';
this.records=[];
//
this._selected_=-1;
// ajax
this.url = options.url;
// ajax
this.urlParams = options.urlParams;
// ajax
this.dataRoot=options.dataRoot;
// ajax
this.delay = options.delay;
// ,
this.start=0;
this.limit = options.limit;
//
this.minChars = options.minChars;
//
this.valueField = options.valueField;
//
this.displayField = options.displayField;
//
this.textField=options.textField || (name+"name");
//
this.queryField = options.queryField || this.displayField;
//
this.tpl = "<li class='item' data-index='<% i %>'>" + (options.tpl || '<% item.'+this.displayField+'%>') + "</li>";
// placeholder
var ph = options.placeholder;
if (ph) {
_support_placeholder ? $element.attr("placeholder", ph)
: this.placeholder = ph;
} else {
if (!_support_placeholder) {
ph = $element.attr("placeholder");
if (ph) {
this.placeholder = ph;
$element.removeAttr("placeholder");
}
}
}
//
this._readOnly=false;
options.readOnly || $element.prop("readOnly") ? this.readOnly(true) : false;
// DOM
this.$hidden = $("<input type='hidden' ignoreElement='true' name='"+ name + "' />").appendTo(input.parentNode);
// DOM
this.$list=$('<div class="c-list auto-list{cls}"><ul></ul></div>'.replace(/{cls}/,options.listCls ? ' '+ options.listCls: '')).appendTo(document.body);
options.listWidth && this.$list.width(options.listWidth==='force' ? this.$element.outerWidth() - (this.$list.outerWidth() - this.$list.width()) : options.listWidth);
options.listHeight && this.$list.height(options.listHeight);
//
this._use_cache = !!options.useCache;
if (this._use_cache) {
this._cache_data = {};
this._cache_length = 0;
this._cache_size = options.cacheSize;
}
// set value
this.reset(options.value || '');
//
$element.focus(function() {
var sf=self;
//
if(sf.isReadOnly()){return true;}
var $this=$(this);
//
if ($this.hasClass("focus")) {return true;};
//
$this.removeClass("placeholder").addClass("focus");
// placeholder (IE6/7/8)
var ph=sf.placeholder,v=this.value;
ph && v === ph ? this.value='' : sf.selectRange(0,v.length);
//
sf.trigger("focus");
// DOM
if(sf.isActive()){return true;}
// DOOM
sf.records.length && sf.show();
}).blur(function() {
var sf=self;
//
if(sf.isReadOnly()){return true;}
// IE doesn't prevent moving focus even with event.preventDefault()
if (sf._ignore_blur_) {this.focus();sf.selectRange(0, this.value.length);sf._ignore_blur_ = false;return false;}
//
$(this).removeClass("focus");
//
sf.value(sf._selected_!=-1 ? sf.records[sf._selected_] : null);
//
sf.hide();
//
sf.trigger("blur");
//
sf.validate(sf.value());
}).click(function() {
var sf=self;
//
if(sf.isReadOnly()){return true;}
// DOM
if(sf.isActive()){return true;}
// DOOM
sf.records.length && sf.show();
}).keydown(function(e) {
var sf=self;
//
if(sf.isReadOnly()){return false;}
switch (e.keyCode) {
case 38: // up
sf.isActive() && sf.focus('prev');
return false;
case 40: // down
sf.isActive() ? sf.focus('next') : ( sf.records.length ? sf.show() : sf.activate());
return false;
case 9: // tab
return true;
case 13: // enter
sf.isActive() && sf.select() && sf.selectRange(0, this.value.length);
return false;
case 27: // escape
// Different browsers have different default behavior for escape
// Single press can mean undo or clear
// Double press in IE means clear the whole form
// e.preventDefault();
sf.isActive() && sf.hide();
return false;
case 37 : // left
sf.isActive() && sf.hide();
return false;
case 39 : // right
!sf.isActive() && !sf.records.length && sf.activate();
return false;
case 33 : // page up
sf.isActive() && sf.focus('pageprev');
return false;
case 34 : // page down
sf.isActive() && sf.focus('pagenext');
return false;
case 36 : // home
sf.isActive() && sf.focus('first');
return false;
case 35 : // end
sf.isActive() && sf.focus('last');
return false;
default:
sf._selected_ = -1;
sf._activate_timer_ && clearTimeout(sf._activate_timer_);
sf._activate_timer_ = setTimeout(function() {sf.activate();}, sf.delay);
}
}).on("input.autocomplete",function(){
// FIREFOX
var sf=self;
if(sf.isReadOnly()){return false;}
sf._selected_ = -1;
sf._activate_timer_ && clearTimeout(sf._activate_timer_);
sf._activate_timer_ = setTimeout(function() {sf.activate();}, sf.delay);
});
// ,
var celleditor=options.celleditor;
this.$list.on("mouseover.autocomplete", " > ul > li", function(e) {
self.focus($(this));
}).on("click.autocomplete", " > ul > li", function(e) {
self.select($(this));
}).mousedown(function(e) {
// prevent moving focus out of the text field
e.preventDefault();
// @IE6/7/8 even prevent default behavior , blur event triggered also
self._ignore_blur_ = $.browser.msie && (+$.browser.version) < 9;
// ,
return celleditor ? false : true;
}).scroll(function(e){return false;});
};
// DOM
$.autocomplete.prototype.isActive = function() {return this.$list[0].style.display === 'block';};
// DOM
$.autocomplete.prototype.show = function() {
//
if(this.isReadOnly()){return this;}
// DOM
this.position();
// N
this.focus(this._selected_ == -1 ? 0 : this._selected_);
//
!this.isActive() && this.trigger("active");
return this;
};
// DOM
$.autocomplete.prototype.hide = function() {
//
if(this.isReadOnly()){return this;}
//
if(!this.isActive()){return this;}
this.$list[0].style.display = 'none';
//
this.trigger("deactive");
return this;
}
// DOM
$.autocomplete.prototype.position=function(){
this.$list[0].style.display='block';
// jQuery position
if ($.ui && $.fn.position) {
this.$list.position( {
my : "left top",
at : "left bottom",
collision : "fit",
of : this.$element
});
return this;
}
// ,
var offset = this.$element.offset();
this.$list.css( {
top : (offset.top + this.$element[0].offsetHeight) + 'px',
left : offset.left + 'px'
});
return this;
};
//
$.autocomplete.prototype.activate = function() {
var text = this.$element[0].value;
// IE6/7/8
this.placeholder && text == this.placeholder ? text = '' : false;
//
text=text.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
//
if(this._last_query===text){return this;}
//
if(this.minChars && this.minChars > text.length){return this;}
this._last_query=text;
//
var opts={};
opts[this.queryField]=text;
//
if(this.limit > 0){
opts.start= this.start ;
opts.limit=this.limit;
}
//
this.load(opts);
return this;
};
// DOM
$.autocomplete.prototype.notice = function(msg) {
var $ul=this.$list.find("> ul");
//
if(msg==='load'){
$ul.html("<li class='notice load'>"+$.i18n.autocomplete.LOAD_TEXT+"</li>");
return this;
}
//
if(msg==='empty'){
$ul.html("<li class='notice empty'>"+$.i18n.autocomplete.NO_DATA+"</li>");
return this;
}
$ul.html(msg);
return this;
};
//
$.autocomplete.prototype.load = function(opts, callback) {
var self = this,cache,cacheKey;
// ajax
this._ajax_request && this._ajax_request.abort();
typeof opts == 'function' ? (callback=opts,opts={}) : false;
// &&
opts=$.extend(true,{},this.urlParams,opts);
//
this.trigger("beforeload",opts);
//
cacheKey=this.queryField && opts[this.queryField];
//
if(opts.postdata && typeof opts.postdata == 'object'){
var sc=opts.searchcondition=opts.searchcondition || {};
this.queryField ? sc[this.queryField]=opts[this.queryField] : false;
$.toJSON ? opts.postdata=$.toJSON(opts.postdata) : false;
}
//
var ajaxCallback = function(data) {
var sf=self,dataRoot=self.dataRoot;
data && dataRoot && data[dataRoot] ? data=data[dataRoot] : false;
data= data || [];
sf._selected_= -1;
//
sf.writeCache(cacheKey, data);
//
typeof callback == 'function' && callback.call(sf, data);
sf.json2Html(data);
//
sf.trigger("load",data);
sf.isActive() && sf.show();
};
//
this.notice('load').show();
//
cache=cacheKey && this.readCache(cacheKey);
if(cache){ajaxCallback(cache);return this;}
var url=opts.url || this.url;
delete opts.url;
// ajax
this._ajax_request=$.ajax( {
url : url,
data : opts,
method : 'POST',
dataType : 'json',
success : ajaxCallback,
error : function() {ajaxCallback(false);}
});
return this;
};
//
$.autocomplete.prototype.focus = function($item) {
var cls="item-selected",$ul=this.$list.find("> ul");
//
if($item instanceof jQuery){
!$item.hasClass(cls) && $ul.find(" > li."+cls).removeClass(cls);
}
//
if(typeof $item == 'number'){
$item=$ul.find(" > li.item:eq("+$item+")");
if(!$item.length){return this;}
!$item.hasClass(cls) && $ul.find(" > li."+cls).removeClass(cls);
}
//
if($item == 'next'){
$item=$ul.find(" > li."+cls).removeClass(cls);
if(!$item.length){return this;}
$item=$item.next();
!$item.length ? $item=$ul.find("> li.item:eq(0)") : false;
}
//
if($item == 'prev'){
$item=$ul.find(" > li."+cls).removeClass(cls);
if(!$item.length){return this;}
$item=$item.prev();
!$item.length ? $item=$ul.find("> li.item:last-child") : false;
}
//
if($item == 'first'){
$ul.find(" > li."+cls).removeClass(cls);
$item=$ul.find(" > li.item:eq(0)");
if(!$item.length){return this;}
}
//
if($item == 'last'){
$ul.find(" > li."+cls).removeClass(cls);
$item=$ul.find("> li.item:last-child");
if(!$item.length){return this;}
}
//
if($item == 'pageprev'){
return this.focus("first");
}
//
if($item == 'pagenext'){
return this.focus("last");
}
if(!($item instanceof jQuery) || !$item.length){return this;}
$item.addClass(cls);
this.scroll($item);
return this;
};
//
$.autocomplete.prototype.scroll = function( $item ) {
var $list=this.$list,list=$list[0];
if($list.outerHeight() >= $list.prop( "scrollHeight" )){return this;}
var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
var borderTop = parseFloat( $.css( list, "borderTopWidth" ) ) || 0,paddingTop = parseFloat( $.css( list, "paddingTop" ) ) || 0;
var offset = $item.offset().top - $list.offset().top - borderTop - paddingTop,scroll = $list.scrollTop();
var elementHeight = $list.height(),itemHeight = $item.outerHeight();
if ( offset < 0 ) {
$list.scrollTop( scroll + offset + 17 );
} else {
if ( offset + itemHeight > elementHeight ) {
//
var scrollbar=0;
if($list.outerWidth() <= $list.prop( "scrollWidth" )){scrollbar=$.position && $.position.scrollbarWidth && $.position.scrollbarWidth() || 17;}
$list.scrollTop( scroll + offset - elementHeight + itemHeight + scrollbar );
}
}
return this;
},
//
$.autocomplete.prototype.select = function($item) {
//
var index = typeof $item =='number' ? $item : -1;
// enter key press
$item === undefined && this.isActive() ? $item = this.$list.find("> ul > li.item-selected") : false;
// DOM
$item instanceof jQuery ? index = +$item.data("index") : false;
var record=this.records[index];
this._selected_ !== index && this.trigger("beforeselect",record);
this.$hidden[0].value = record[this.valueField];
this.$element[0].value = record[this.displayField];
this._selected_ !== index && this.trigger("select",record);
this._selected_ = index;
this.hide();
return this;
};
// MY GOD DOM
$.autocomplete.prototype.selectRange = function(start, end) {
var input = this.$element[0];
if (input.setSelectionRange) {
input.focus();
input.setSelectionRange(start, end);
} else if (input.createTextRange) {
var range = input.createTextRange();
range.collapse(true);
range.moveEnd('character', end);
range.moveStart('character', start);
range.select();
}
return this;
};
//
$.autocomplete.prototype.find = function(key, value) {
var records=this.records || [],l = records.length;
//
if (typeof key == 'number')
return l > key ? records[key] : null;
// , name:'tom'
while (l--)
if (records[l][key] == value)
return records[l];
return null;
};
//
$.autocomplete.prototype.indexOf = function(key, value) {
//
arguments.length == 1 && (value=key,key=this.valueField);
//
typeof value == 'object' ? value = value[this.valueField] : false;
var records=this.records || [],l = records.length;
while(l--)
if(records[l][key]== value)
return l;
return -1;
};
//
$.autocomplete.prototype.readCache = function(cacheKey) {
this._cache_data=this._cache_data || {};
if(this._use_cache && cacheKey){
var cache=this._cache_data[cacheKey];
//
cache ? cache.count=(cache.count || 0) + 1 : false;
return cache ? cache.data : null;
}
return null;
};
//
$.autocomplete.prototype.writeCache = function(cacheKey, data) {
this._cache_data=this._cache_data || {};
if(this._use_cache && cacheKey && data){
// , ,
if(this._cache_length > this._cache_size){
this._cache_data={};
this._cache_length=0;
}
this._cache_length++;
this._cache_data[cacheKey]={data:data,count:0};
}
return this;
};
// JSON HTML
$.autocomplete.prototype.json2Html = function(json, template) {
var tplEngine = function(tpl, data) {
var reg = /<%([^%>]+)?%>/g,
regOut = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g,
code = 'var r=[];
',
cursor = 0;
var add = function(line, js) {
js? (code += line.match(regOut) ? line + '
' : 'r.push(' + line + ');
') :
(code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");
' : '');
return add;
}
while(match = reg.exec(tpl)) {
add(tpl.slice(cursor, match.index))(match[1], true);
cursor = match.index + match[0].length;
}
add(tpl.substr(cursor, tpl.length - cursor));
code += 'return r.join("");';
return new Function(code.replace(/[\r\t
]/g, '')).apply(data);
};
template=template || this.tpl;
var code='<% for(var i=0,l=this && this.length || 0;i<l;i++){var item=this[i]; %>'+
template+
'<% } %>';
this.records=json;
this.notice(json.length===0 ? 'empty' : tplEngine(code,json));
return this;
}
//
$.autocomplete.prototype.text=function(){
var text=this.$element[0].value;
return this.placeholder && text===this.placeholder ? "" : text;
};
//
$.autocomplete.prototype.validate=function(){return $.validate ? $.validate(this.$element) : true;};
//
$.autocomplete.prototype.isReadOnly=function(){return this._read_only===true;};
//
$.autocomplete.prototype.readOnly = function(readOnly) {
readOnly=!!readOnly;
this._read_only=readOnly;
this.$element[readOnly ? "addClass" : "removeClass"]("readOnly").prop("readOnly",readOnly);
return this;
};
//
$.autocomplete.prototype.value = function(value,text) {
// getter method
if (value === undefined) {return this.$hidden[0].value;}
//
value===null ? value='' : false;
var oldValue = this.value();
//
var index = value === '' ? -1 : this.indexOf(value);
var record = index != -1 ? this.records[index] : null;
//
if(record == null && value ){
record = value;
if(typeof value == 'string'){
record={};
record[this.valueField] = value;
record[this.displayField] = text || value;
}
// DOM
if(!this.records.length){
index = 0;
this.json2Html([record]);
}
}
value = record ? record[this.valueField] : '';
text = record ? record[this.displayField] : '';
this._selected_ = index;
this.$hidden[0].value = value;
var ph=this.placeholder;
// placeholder
this.$element[0].value = ph && text==="" ? ph : text;
ph && this.$element[(text ? "remove" : "add") + "Class"]("placeholder");
var newValue = this.value();
//
oldValue != newValue && this.trigger("select",record);
//
oldValue != newValue && this.trigger("change",newValue,oldValue);
return this;
};
//
$.autocomplete.prototype.reset = function(value,text) {
this._last_query='';
this._default_value= value != null && typeof value == 'object' ? value[this.valueField] : value;
//
this._default_value = this._default_value === undefined ? '' : ''+this._default_value;
this.value(value,text);
return this;
};
//
$.autocomplete.prototype.addChild = function(child) {
this.children=this.children || [];
for(var i=0,l=this.children.length;i<l;i++)
if(this.children[i]===child)
break;
l==0 || i===l ? (child.parent=this,this.children.push(child)) : false;
return this;
};
//
$.autocomplete.prototype.removeChild = function(child) {
this.children=this.children || [];
for(var i=0,l=this.children.length;i<l;i++)
if(this.children[i]===child)
break;
i!==l ? (child.parent=null,this.children.splice(i,1)) : false;
return this;
};
//
$.autocomplete.prototype.hasChild = function() {
return this.children && this.children.length > 0;
};
$.autocomplete.prototype.trigger = function(event) {
var params = [ this];
for ( var i = 1, l = arguments.length; i < l; i++) {
params[i] = arguments[i];
}
var type=event.toUpperCase();
event=new jQuery.Event(type, type+"."+this.widget );
this.$element.triggerHandler(event, params);
return event;
};
$.autocomplete.prototype.destroy=function(keep){
this.$list.off(".autocomplete").remove();
this.$element.off(".autocomplete").removeData();
this.off();
var name=this.$hidden[0].name;
this.$hidden.remove();
!!keep ? this.$element[0].name=name : this.$element.remove();
};
$.autocomplete.prototype.on = function(event, handler) {
this.$element.on(event.toUpperCase() + "." + this.widget, handler);
return this;
};
$.autocomplete.prototype.one = function(event, handler) {
this.$element.one(event.toUpperCase() + "." + this.widget, handler);
return this;
};
$.autocomplete.prototype.off = function(event, handler) {
this.$element.off(((event && event.toUpperCase()) || '') + "." + this.widget, handler);
return this;
};
$.autocomplete.prototype.widget = 'autocomplete';
$.fn.autocomplete = function(options) {
var f=null,fields=[];
for(var i=0,l=this.length;i<l;i++){
f=$.data(this[i], "widget");
!f ? (f=new $.autocomplete(this.eq(i), $.extend( {}, $.fn.autocomplete.defaults, options)),$.data(this[i], "widget", f)) : false;
fields[i]=f;
}
return l===1 ? fields[0] : fields;
};
/**
* Default options for autocomplete plugin
*/
$.fn.autocomplete.defaults = {
dataRoot:'dataroot',
/** **/
minChars : 1,
/** **/
name : '',
/** class input **/
cls : '',
/** class **/
listCls : '',
/** , auto **/
listHeight:'',
/** , **/
listWidth:'force',
/** **/
delay : 250,
/** **/
tpl : '',
/** **/
displayField : 'name',
/** **/
valueField : 'code',
/** **/
textField:'',
/** **/
queryField : '',
/** **/
url : '',
urlParams : {},
/** **/
limit : 10,
/** **/
useCache : true,
cacheSize : 10
};
})(jQuery);
:
<input type='text' id='remote' />
<script>
// JSON :result:{data:[{name:' ',code:'1'},{name:' ',code:'2'}]}
$("#remote").combobox({url:'getSexData.action',dataRoot:'result'});
</script>