Fix overlapping ids, support loading more items in hint
parent
01cad41a87
commit
2faa1f7732
|
@ -1,12 +1,12 @@
|
||||||
.hintLayer {
|
.hintLayer {
|
||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
color: gray;
|
color: gray;
|
||||||
width: 200pt;
|
|
||||||
background-color: white;
|
background-color: white;
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
max-height: 300pt;
|
max-height: 300pt;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
overflow: -moz-scrollbars-vertical;
|
overflow: -moz-scrollbars-vertical;
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
.hintEmptyText {
|
.hintEmptyText {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
|
@ -16,15 +16,20 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 1px 3px;
|
padding: 1px 3px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.hintActiveItem {
|
.hintActiveItem {
|
||||||
color: white;
|
color: white;
|
||||||
background-color: #008;
|
background-color: #008;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 1px 3px;
|
padding: 1px 3px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.hintItem input, .hintActiveItem input {
|
.hintItem input, .hintActiveItem input {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
|
.hintItem img, .hintActiveItem img {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
73
hinter.js
73
hinter.js
|
@ -11,11 +11,21 @@
|
||||||
Parameters:
|
Parameters:
|
||||||
input
|
input
|
||||||
The input, either id or DOM element reference (the input must have an id anyway).
|
The input, either id or DOM element reference (the input must have an id anyway).
|
||||||
dataLoader(hint, value)
|
dataLoader(hint, value[, more])
|
||||||
Callback which should load autocomplete options and then call
|
Callback which should load autocomplete options and then call
|
||||||
hint.replaceItems([ [ name, value ], [ name, value ], ... ])
|
hint.replaceItems(newOptions, keepPosition), where:
|
||||||
'hint' parameter will be this autocompleter object, and the guess
|
newOptions is [ [ name, value ], [ name, value ], ... ] and
|
||||||
should be done based on 'value' parameter (string).
|
keepPosition should be true when more was > 0.
|
||||||
|
Callback parameters:
|
||||||
|
hint
|
||||||
|
This SimpleAutocomplete object
|
||||||
|
value
|
||||||
|
The string guess should be done based on
|
||||||
|
more
|
||||||
|
This is optional and means the times user has triggered 'load more items'.
|
||||||
|
i.e. if the original list had 10 items (more=0), after first 'more' click
|
||||||
|
user would expect 20 items (more=1), and etc.
|
||||||
|
See also moreMarker option below.
|
||||||
|
|
||||||
params attribute is an object with optional parameters:
|
params attribute is an object with optional parameters:
|
||||||
multipleDelimiter
|
multipleDelimiter
|
||||||
|
@ -39,6 +49,14 @@
|
||||||
delay
|
delay
|
||||||
If this is set to a non-zero value, the autocompleter does no more than
|
If this is set to a non-zero value, the autocompleter does no more than
|
||||||
1 request in each delay milliseconds.
|
1 request in each delay milliseconds.
|
||||||
|
moreMarker
|
||||||
|
The server supplying hint options usually limits their count.
|
||||||
|
But it's not always convenient having to type additional characters for
|
||||||
|
narrowing down the selection. Optionally you can supply additional item
|
||||||
|
with special value '#MORE' (or 'moreMarker' option value if it is there)
|
||||||
|
at the end of the list, and when it will be clicked, SimpleAutocomplete
|
||||||
|
will issue another request to dataLoader with incremented 'more' parameter.
|
||||||
|
You can also set moreMarker to false to disable this feature.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// *** Constructor ***
|
// *** Constructor ***
|
||||||
|
@ -59,18 +77,24 @@ var SimpleAutocomplete = function(input, dataLoader, params)
|
||||||
this.emptyText = params.emptyText;
|
this.emptyText = params.emptyText;
|
||||||
this.allowHTML = params.allowHTML;
|
this.allowHTML = params.allowHTML;
|
||||||
this.delay = params.delay;
|
this.delay = params.delay;
|
||||||
|
this.moreMarker = params.moreMarker;
|
||||||
|
|
||||||
|
// Default values
|
||||||
|
if (this.moreMarker === undefined)
|
||||||
|
this.moreMarker = '#MORE';
|
||||||
if (this.delay === undefined)
|
if (this.delay === undefined)
|
||||||
this.delay = 300;
|
this.delay = 300;
|
||||||
if (this.emptyText === undefined)
|
if (this.emptyText === undefined)
|
||||||
this.emptyText = 'No items found';
|
this.emptyText = 'No items found';
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
|
this.isMoreClick = false;
|
||||||
|
this.more = 0;
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
this.closure = [];
|
this.closure = [];
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this.skipHideCounter = 0;
|
this.skipHideCounter = 0;
|
||||||
this.selectedIndex = -1;
|
this.selectedIndex = -1;
|
||||||
this.id = input.id;
|
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
|
|
||||||
// *** Call initialise ***
|
// *** Call initialise ***
|
||||||
|
@ -83,6 +107,10 @@ var SimpleAutocomplete = function(input, dataLoader, params)
|
||||||
SimpleAutocomplete.prototype.init = function()
|
SimpleAutocomplete.prototype.init = function()
|
||||||
{
|
{
|
||||||
var e = this.input;
|
var e = this.input;
|
||||||
|
var l = SimpleAutocomplete.SimpleAutocompletes;
|
||||||
|
this.id = this.input.id + l.length;
|
||||||
|
l.push(this);
|
||||||
|
|
||||||
var p = getOffset(e);
|
var p = getOffset(e);
|
||||||
|
|
||||||
// Create hint layer
|
// Create hint layer
|
||||||
|
@ -106,7 +134,6 @@ SimpleAutocomplete.prototype.init = function()
|
||||||
// Remember instance
|
// Remember instance
|
||||||
e.SimpleAutocomplete_input = this;
|
e.SimpleAutocomplete_input = this;
|
||||||
t.SimpleAutocomplete_layer = this;
|
t.SimpleAutocomplete_layer = this;
|
||||||
SimpleAutocomplete.SimpleAutocompletes.push(this);
|
|
||||||
|
|
||||||
// Set event listeners
|
// Set event listeners
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -127,10 +154,13 @@ SimpleAutocomplete.prototype.init = function()
|
||||||
};
|
};
|
||||||
|
|
||||||
// obj = [ [ name, value, disabled ], [ name, value ], ... ]
|
// obj = [ [ name, value, disabled ], [ name, value ], ... ]
|
||||||
SimpleAutocomplete.prototype.replaceItems = function(items)
|
SimpleAutocomplete.prototype.replaceItems = function(items, keepPosition)
|
||||||
{
|
{
|
||||||
this.hintLayer.innerHTML = '';
|
this.hintLayer.innerHTML = '';
|
||||||
|
if (!keepPosition)
|
||||||
|
{
|
||||||
this.hintLayer.scrollTop = 0;
|
this.hintLayer.scrollTop = 0;
|
||||||
|
}
|
||||||
this.items = [];
|
this.items = [];
|
||||||
if (!items || items.length == 0)
|
if (!items || items.length == 0)
|
||||||
{
|
{
|
||||||
|
@ -203,6 +233,8 @@ SimpleAutocomplete.prototype.makeItem = function(name, value, checked)
|
||||||
d.title = value;
|
d.title = value;
|
||||||
if (this.allowHTML)
|
if (this.allowHTML)
|
||||||
d.innerHTML = name;
|
d.innerHTML = name;
|
||||||
|
else
|
||||||
|
d.appendChild(document.createTextNode(name));
|
||||||
if (this.multipleDelimiter)
|
if (this.multipleDelimiter)
|
||||||
{
|
{
|
||||||
var c = document.createElement('input');
|
var c = document.createElement('input');
|
||||||
|
@ -216,8 +248,6 @@ SimpleAutocomplete.prototype.makeItem = function(name, value, checked)
|
||||||
d.appendChild(c);
|
d.appendChild(c);
|
||||||
addListener(c, 'click', this.preventCheck);
|
addListener(c, 'click', this.preventCheck);
|
||||||
}
|
}
|
||||||
if (!this.allowHTML)
|
|
||||||
d.appendChild(document.createTextNode(name));
|
|
||||||
var self = this;
|
var self = this;
|
||||||
addListener(d, 'mouseover', function() { return self.onItemMouseOver(this); });
|
addListener(d, 'mouseover', function() { return self.onItemMouseOver(this); });
|
||||||
addListener(d, 'mousedown', function(ev) { return self.onItemClick(ev, this); });
|
addListener(d, 'mousedown', function(ev) { return self.onItemClick(ev, this); });
|
||||||
|
@ -275,6 +305,7 @@ SimpleAutocomplete.prototype.selectItem = function(index)
|
||||||
var old = this.input.value.split(this.multipleDelimiter);
|
var old = this.input.value.split(this.multipleDelimiter);
|
||||||
for (var i = 0; i < old.length; i++)
|
for (var i = 0; i < old.length; i++)
|
||||||
old[i] = old[i].trim();
|
old[i] = old[i].trim();
|
||||||
|
// Turn the clicked item on or off, preserving order
|
||||||
if (!this.items[index][2])
|
if (!this.items[index][2])
|
||||||
{
|
{
|
||||||
for (var i = old.length-1; i >= 0; i--)
|
for (var i = old.length-1; i >= 0; i--)
|
||||||
|
@ -376,7 +407,17 @@ SimpleAutocomplete.prototype.onItemMouseOver = function(elm)
|
||||||
// Handle item clicks
|
// Handle item clicks
|
||||||
SimpleAutocomplete.prototype.onItemClick = function(ev, elm)
|
SimpleAutocomplete.prototype.onItemClick = function(ev, elm)
|
||||||
{
|
{
|
||||||
this.selectItem(parseInt(elm.id.substr(this.id.length+6)));
|
var index = parseInt(elm.id.substr(this.id.length+6));
|
||||||
|
if (this.moreMarker && this.items[index][1] == this.moreMarker)
|
||||||
|
{
|
||||||
|
// User clicked 'more'. Load more items without delay.
|
||||||
|
this.isMoreClick = true;
|
||||||
|
this.more++;
|
||||||
|
this.onChange();
|
||||||
|
this.isMoreClick = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.selectItem(index);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -384,16 +425,20 @@ SimpleAutocomplete.prototype.onItemClick = function(ev, elm)
|
||||||
SimpleAutocomplete.prototype.onChange = function()
|
SimpleAutocomplete.prototype.onChange = function()
|
||||||
{
|
{
|
||||||
var v = this.input.value.trim();
|
var v = this.input.value.trim();
|
||||||
if (v != this.curValue)
|
if (!this.isMoreClick)
|
||||||
|
{
|
||||||
|
this.more = 0;
|
||||||
|
}
|
||||||
|
if (v != this.curValue || this.isMoreClick)
|
||||||
{
|
{
|
||||||
this.curValue = v;
|
this.curValue = v;
|
||||||
if (!this.delay)
|
if (!this.delay || this.isMoreClick)
|
||||||
this.dataLoader(this, v);
|
this.dataLoader(this, v, this.more);
|
||||||
else if (!this.timer)
|
else if (!this.timer)
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
this.timer = setTimeout(function() {
|
this.timer = setTimeout(function() {
|
||||||
self.dataLoader(self, self.curValue);
|
self.dataLoader(self, self.curValue, self.more);
|
||||||
self.timer = null;
|
self.timer = null;
|
||||||
}, this.delay);
|
}, this.delay);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue