From 2faa1f77321936131c7533f9cded9e78ba6b24e8 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Thu, 17 Jan 2013 18:59:14 +0000 Subject: [PATCH] Fix overlapping ids, support loading more items in hint --- hinter.css | 7 ++++- hinter.js | 75 +++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/hinter.css b/hinter.css index 1a63e56..9abe905 100644 --- a/hinter.css +++ b/hinter.css @@ -1,12 +1,12 @@ .hintLayer { border: 1px solid gray; color: gray; - width: 200pt; background-color: white; font-size: 80%; max-height: 300pt; overflow-y: scroll; overflow: -moz-scrollbars-vertical; + z-index: 1000; } .hintEmptyText { padding: 3px; @@ -16,15 +16,20 @@ cursor: pointer; padding: 1px 3px; vertical-align: middle; + white-space: nowrap; } .hintActiveItem { color: white; background-color: #008; cursor: pointer; padding: 1px 3px; + white-space: nowrap; } .hintItem input, .hintActiveItem input { cursor: pointer; vertical-align: middle; margin-right: 3px; } +.hintItem img, .hintActiveItem img { + vertical-align: middle; +} diff --git a/hinter.js b/hinter.js index 99d56cb..9b1ca74 100644 --- a/hinter.js +++ b/hinter.js @@ -11,11 +11,21 @@ Parameters: input 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 - hint.replaceItems([ [ name, value ], [ name, value ], ... ]) - 'hint' parameter will be this autocompleter object, and the guess - should be done based on 'value' parameter (string). + hint.replaceItems(newOptions, keepPosition), where: + newOptions is [ [ name, value ], [ name, value ], ... ] and + 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: multipleDelimiter @@ -39,6 +49,14 @@ delay If this is set to a non-zero value, the autocompleter does no more than 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 *** @@ -59,18 +77,24 @@ var SimpleAutocomplete = function(input, dataLoader, params) this.emptyText = params.emptyText; this.allowHTML = params.allowHTML; this.delay = params.delay; + this.moreMarker = params.moreMarker; + + // Default values + if (this.moreMarker === undefined) + this.moreMarker = '#MORE'; if (this.delay === undefined) this.delay = 300; if (this.emptyText === undefined) this.emptyText = 'No items found'; // Variables + this.isMoreClick = false; + this.more = 0; this.timer = null; this.closure = []; this.items = []; this.skipHideCounter = 0; this.selectedIndex = -1; - this.id = input.id; this.disabled = false; // *** Call initialise *** @@ -83,6 +107,10 @@ var SimpleAutocomplete = function(input, dataLoader, params) SimpleAutocomplete.prototype.init = function() { var e = this.input; + var l = SimpleAutocomplete.SimpleAutocompletes; + this.id = this.input.id + l.length; + l.push(this); + var p = getOffset(e); // Create hint layer @@ -106,7 +134,6 @@ SimpleAutocomplete.prototype.init = function() // Remember instance e.SimpleAutocomplete_input = this; t.SimpleAutocomplete_layer = this; - SimpleAutocomplete.SimpleAutocompletes.push(this); // Set event listeners var self = this; @@ -127,10 +154,13 @@ SimpleAutocomplete.prototype.init = function() }; // obj = [ [ name, value, disabled ], [ name, value ], ... ] -SimpleAutocomplete.prototype.replaceItems = function(items) +SimpleAutocomplete.prototype.replaceItems = function(items, keepPosition) { this.hintLayer.innerHTML = ''; - this.hintLayer.scrollTop = 0; + if (!keepPosition) + { + this.hintLayer.scrollTop = 0; + } this.items = []; if (!items || items.length == 0) { @@ -203,6 +233,8 @@ SimpleAutocomplete.prototype.makeItem = function(name, value, checked) d.title = value; if (this.allowHTML) d.innerHTML = name; + else + d.appendChild(document.createTextNode(name)); if (this.multipleDelimiter) { var c = document.createElement('input'); @@ -216,8 +248,6 @@ SimpleAutocomplete.prototype.makeItem = function(name, value, checked) d.appendChild(c); addListener(c, 'click', this.preventCheck); } - if (!this.allowHTML) - d.appendChild(document.createTextNode(name)); var self = this; addListener(d, 'mouseover', function() { return self.onItemMouseOver(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); for (var i = 0; i < old.length; i++) old[i] = old[i].trim(); + // Turn the clicked item on or off, preserving order if (!this.items[index][2]) { for (var i = old.length-1; i >= 0; i--) @@ -376,7 +407,17 @@ SimpleAutocomplete.prototype.onItemMouseOver = function(elm) // Handle item clicks 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; }; @@ -384,16 +425,20 @@ SimpleAutocomplete.prototype.onItemClick = function(ev, elm) SimpleAutocomplete.prototype.onChange = function() { 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; - if (!this.delay) - this.dataLoader(this, v); + if (!this.delay || this.isMoreClick) + this.dataLoader(this, v, this.more); else if (!this.timer) { var self = this; this.timer = setTimeout(function() { - self.dataLoader(self, self.curValue); + self.dataLoader(self, self.curValue, self.more); self.timer = null; }, this.delay); }