fruitbat Posted December 7, 2008 Report Share Posted December 7, 2008 (edited) Right, i'm still a Noobie in comparison with most when it comes to stuff like this so here goes.What I am trying to create is a HTML page which our software will display in the form of an Iframe, with 1 input box, and when the user types some text into the input box it looks at a pre defined list of results (which are in the form of 5/6 word questions), and I want a list to populate below the input box with the questions where any words in the questions that have a match to what has been input by the user.Upon clicking a question I want the input box to appear with the complete question that has been clicked from the list.Basically a Predicitve Text Input Box only using my pre-defined results to search on.I have been looking around on t'internet but can't find much help.Anyone got any help with the best way to do this, or even point me in the right direction as to some help pages (latter preferably so I can play with it and work it out). I did see something recently on W3Schools about this recently but can't find it now, plus that example didn't have the click on answer to populate input boxThanks in Advance Frooty Edited December 7, 2008 by fruitbat Quote Link to comment Share on other sites More sharing options...
fruitbat Posted December 7, 2008 Author Report Share Posted December 7, 2008 (edited) Found THIS ONEIt works but the only thing it doesn't do that I would like for this idea to work is search the complete text of the result and return anything with a match, it tries to match from letter 1 onwards and not match from any point throughout the potential results text.Looking at the JS file that can be opened from the bottom of the page, at around line 55 (if I am reading the code right, which I may not be as thats quite a bit above my level at the moment), is where it tells it what to look for to start creating the matching results, anyone good enough to be able to add to/edit this to allow it search from any character in the string rather than letter 1.CheersFrooty Edited December 7, 2008 by fruitbat Quote Link to comment Share on other sites More sharing options...
Village Idiot Posted December 7, 2008 Report Share Posted December 7, 2008 (edited) I'd say you're probably best off with the jquery one you found as it seems to do most of what you want, then modify the search code to look at any matches in all of the text. I'm not familiar with how it works, but I reckon it's worth a look to see if it's do-able rather than starting from scratch.Edit: I'll have a quick look at the code and see if I can figure it out. Edited December 7, 2008 by Village Idiot Quote Link to comment Share on other sites More sharing options...
fruitbat Posted December 7, 2008 Author Report Share Posted December 7, 2008 (edited) I'd say you're probably best off with the jquery one you found as it seems to do most of what you want, then modify the search code to look at any matches in all of the text. I'm not familiar with how it works, but I reckon it's worth a look to see if it's do-able rather than starting from scratch.I've modified the JQuery one to lookup my potential results rather than theirs and return what I require it to, but I can't work out in the JS file how to do the "any matches in all of the text."Frooty Edited December 7, 2008 by fruitbat Quote Link to comment Share on other sites More sharing options...
Village Idiot Posted December 7, 2008 Report Share Posted December 7, 2008 Tried to edit the js file, but is harder than i thought. Managed to get this far...Replaceif( row[0].length > 0 ){     // get the first character     sFirstChar = row[0].substring(0, 1).toLowerCase();     // if no lookup array for this character exists, look it up now     if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];     // if the match is a string     stMatchSets[sFirstChar].push(row); } with // if the length is zero, don't add to list if( row[0].length > 0 ){     var tmpArray = [];     for (var j = 1; j <= row[0].length; j++) {         // get the next character         sNextChar = row[0].substring(j-1, j).toLowerCase();         // Needed to stop duplicate matches appearing in results         var alreadyIndexed = false;         for (var k = 0; k < tmpArray.length; k++){             if(tmpArray[k] == sNextChar) {                 alreadyIndexed =  true;             }         }         // Character has not been skipped         if (!alreadyIndexed) {             tmpArray.push(sNextChar);             // if no lookup array for this character exists, look it up now             if( !stMatchSets[sNextChar] ) stMatchSets[sNextChar] = [];                 // if the match is a string                 stMatchSets[sNextChar].push(row);             }         }     } } ^^ That looks at the whole word rather than just the first character and if( lastKeyPressCode != 8 ){     // fill in the value (keep the case the user has typed)     $input.val($input.val() + sValue.substring(prev.length)); with if( lastKeyPressCode != 8 ){     // fill in the value     $input.val(sValue);^^ That ignores the character what was typed and shows the 'proper' match. Hard to explain, but take it out and you'll see what I mean.That will match things halfway through the word/phrase but only on the first character. I'm not too sure how you can make the rest of what the user types match as well.Like if you type "d" it would match "aberdeen" and "denmark", but then "de" would only match "denmark" whereas it should match aberdeen. Not sue how to get past this bit either, sorry. Quote Link to comment Share on other sites More sharing options...
Village Idiot Posted December 7, 2008 Report Share Posted December 7, 2008 Ok, think I've got it now...jQuery.autocomplete = function(input, options) {     // Create a link to self     var me = this;     // Create jQuery object for input element     var $input = $(input).attr("autocomplete", "off");     // Apply inputClass if necessary     if (options.inputClass) $input.addClass(options.inputClass);     // Create results     var results = document.createElement("div");     // Create jQuery object for results     var $results = $(results);     $results.hide().addClass(options.resultsClass).css("position", "absolute");     if( options.width > 0 ) $results.css("width", options.width);     // Add to body element     $("body").append(results);     input.autocompleter = me;     var timeout = null;     var prev = "";     var active = -1;     var cache = {};     var keyb = false;     var hasFocus = false;     var lastKeyPressCode = null;     // flush cache     function flushCache(){         cache = {};         cache.data = {};         cache.length = 0;     };     // flush cache     flushCache();     // if there is a data array supplied     if( options.data != null ){         var sFirstChar = "", stMatchSets = {}, row = [];         // no url was specified, we need to adjust the cache length to make sure it fits the local data store         if( typeof options.url != "string" ) options.cacheLength = 1;         // loop through the array and create a lookup structure         for( var i=0; i < options.data.length; i++ ){             // if row is a string, make an array otherwise just reference the array             row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);             /*// if the length is zero, don't add to list             if( row[0].length > 0 ){                 // get the first character                 sFirstChar = row[0].substring(0, 1).toLowerCase();                 // if no lookup array for this character exists, look it up now                 if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];                 // if the match is a string                 stMatchSets[sFirstChar].push(row);             }*/                         // if the length is zero, don't add to list             if( row[0].length > 0 ){                 // Go through each character in the data string and index a match                 var tmpArray = [];                 for (var j = 1; j <= row[0].length; j++) {                     // get the next character                     sNextChar = row[0].substring(j-1, j).toLowerCase();                     // Check whether this character already exists in the temp array.                     // If it does, we skip it to ensure the phrase is not added twice                     // to the cache                     var alreadyIndexed = false;                     for (var k = 0; k < tmpArray.length; k++){                       if(tmpArray[k] == sNextChar) {                         alreadyIndexed =  true;                       }                     }                     // Character has not been skipped so index the match                     if (!alreadyIndexed) {                       tmpArray.push(sNextChar);                       // if no lookup array for this character exists, look it up now                         if( !stMatchSets[sNextChar] ) stMatchSets[sNextChar] = [];                         // if the match is a string                         stMatchSets[sNextChar].push(row);                     }                 }             }                                 }         // add the data items to the cache         for( var k in stMatchSets ){             // increase the cache size             options.cacheLength++;             // add to the cache             addToCache(k, stMatchSets[k]);         }     }     $input     .keydown(function(e) {         // track last key pressed         lastKeyPressCode = e.keyCode;         switch(e.keyCode) {             case 38: // up                 e.preventDefault();                 moveSelect(-1);                 break;             case 40: // down                 e.preventDefault();                 moveSelect(1);                 break;             case 9:  // tab             case 13: // return                 if( selectCurrent() ){                     // make sure to blur off the current field                     $input.get(0).blur();                     e.preventDefault();                 }                 break;             default:                 active = -1;                 if (timeout) clearTimeout(timeout);                 timeout = setTimeout(function(){onChange();}, options.delay);                 break;         }     })     .focus(function(){         // track whether the field has focus, we shouldn't process any results if the field no longer has focus         hasFocus = true;     })     .blur(function() {         // track whether the field has focus         hasFocus = false;         hideResults();     });     hideResultsNow();     function onChange() {         // ignore if the following keys are pressed: [del] [shift] [capslock]         if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();         var v = $input.val();         if (v == prev) return;         prev = v;         if (v.length >= options.minChars) {             $input.addClass(options.loadingClass);             requestData(v);         } else {             $input.removeClass(options.loadingClass);             $results.hide();         }     };     function moveSelect(step) {         var lis = $("li", results);         if (!lis) return;         active += step;         if (active < 0) {             active = 0;         } else if (active >= lis.size()) {             active = lis.size() - 1;         }         lis.removeClass("ac_over");         $(lis[active]).addClass("ac_over");         // Weird behaviour in IE         // if (lis[active] && lis[active].scrollIntoView) {         //     lis[active].scrollIntoView(false);         // }     };     function selectCurrent() {         var li = $("li.ac_over", results)[0];         if (!li) {             var $li = $("li", results);             if (options.selectOnly) {                 if ($li.length == 1) li = $li[0];             } else if (options.selectFirst) {                 li = $li[0];             }         }         if (li) {             selectItem(li);             return true;         } else {             return false;         }     };     function selectItem(li) {         if (!li) {             li = document.createElement("li");             li.extra = [];             li.selectValue = "";         }         var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);         input.lastSelected = v;         prev = v;         $results.html("");         $input.val(v);         hideResultsNow();         if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li) }, 1);     };     // selects a portion of the input string     function createSelection(start, end){         // get a reference to the input element         var field = $input.get(0);         if( field.createTextRange ){             var selRange = field.createTextRange();             selRange.collapse(true);             selRange.moveStart("character", start);             selRange.moveEnd("character", end);             selRange.select();         } else if( field.setSelectionRange ){             field.setSelectionRange(start, end);         } else {             if( field.selectionStart ){                 field.selectionStart = start;                 field.selectionEnd = end;             }         }         field.focus();     };     // fills in the input box w/the first match (assumed to be the best match)     function autoFill(sValue){         // if the last user key pressed was backspace, don't autofill         if( lastKeyPressCode != 8 ){             //$input.val(sValue);             // select the portion of the value not typed by the user (so the next character will erase)             //createSelection(prev.length, sValue.length);         }     };     /*     // fills in the input box w/the first match (assumed to be the best match)     function autoFill(sValue){         // if the last user key pressed was backspace, don't autofill         if( lastKeyPressCode != 8 ){             // fill in the value (keep the case the user has typed)             $input.val($input.val() + sValue.substring(prev.length));             // select the portion of the value not typed by the user (so the next character will erase)             createSelection(prev.length, sValue.length);         }     };*/     function showResults() {         // get the position of the input field right now (in case the DOM is shifted)         var pos = findPos(input);         // either use the specified width, or autocalculate based on form element         var iWidth = (options.width > 0) ? options.width : $input.width();         // reposition         $results.css({             width: parseInt(iWidth) + "px",             top: (pos.y + input.offsetHeight) + "px",             left: pos.x + "px"         }).show();     };     function hideResults() {         if (timeout) clearTimeout(timeout);         timeout = setTimeout(hideResultsNow, 200);     };     function hideResultsNow() {         if (timeout) clearTimeout(timeout);         $input.removeClass(options.loadingClass);         if ($results.is(":visible")) {             $results.hide();         }         if (options.mustMatch) {             var v = $input.val();             if (v != input.lastSelected) {                 selectItem(null);             }         }     };     function receiveData(q, data) {         if (data) {             $input.removeClass(options.loadingClass);             results.innerHTML = "";             // if the field no longer has focus or if there are no matches, do not display the drop down             if( !hasFocus || data.length == 0 ) return hideResultsNow();             if ($.browser.msie) {                 // we put a styled iframe behind the calendar so HTML SELECT elements don't show through                 $results.append(document.createElement('iframe'));             }             results.appendChild(dataToDom(data));             // autofill in the complete box w/the first match as long as the user hasn't entered in more data             if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);             showResults();         } else {             hideResultsNow();         }     };     function parseData(data) {         if (!data) return null;         var parsed = [];         var rows = data.split(options.lineSeparator);         for (var i=0; i < rows.length; i++) {             var row = $.trim(rows[i]);             if (row) {                 parsed[parsed.length] = row.split(options.cellSeparator);             }         }         return parsed;     };     function dataToDom(data) {         var ul = document.createElement("ul");         var num = data.length;         // limited results to a max number         if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;         for (var i=0; i < num; i++) {             var row = data[i];             if (!row) continue;             var li = document.createElement("li");             if (options.formatItem) {                 li.innerHTML = options.formatItem(row, i, num);                 li.selectValue = row[0];             } else {                 li.innerHTML = row[0];                 li.selectValue = row[0];             }             var extra = null;             if (row.length > 1) {                 extra = [];                 for (var j=1; j < row.length; j++) {                     extra[extra.length] = row[j];                 }             }             li.extra = extra;             ul.appendChild(li);             $(li).hover(                 function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); },                 function() { $(this).removeClass("ac_over"); }             ).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) });         }         return ul;     };     function requestData(q) {         if (!options.matchCase) q = q.toLowerCase();         var data = options.cacheLength ? loadFromCache(q) : null;         // recieve the cached data         if (data) {             receiveData(q, data);         // if an AJAX url has been supplied, try loading the data now         } else if( (typeof options.url == "string") && (options.url.length > 0) ){             $.get(makeUrl(q), function(data) {                 data = parseData(data);                 addToCache(q, data);                 receiveData(q, data);             });         // if there's been no data found, remove the loading class         } else {             $input.removeClass(options.loadingClass);         }     };     function makeUrl(q) {         var url = options.url + "?q=" + encodeURI(q);         for (var i in options.extraParams) {             url += "&" + i + "=" + encodeURI(options.extraParams[i]);         }         return url;     };     function loadFromCache(q) {         if (!q) return null;         if (cache.data[q]) return cache.data[q];         if (options.matchSubset) {             for (var i = q.length - 1; i >= options.minChars; i--) {                 var qs = q.substr(0, i);                 var c = cache.data[qs];                 if (c) {                     var csub = [];                     for (var j = 0; j < c.length; j++) {                         var x = c[j];                         var x0 = x[0];                         if (matchSubset(x0, q)) {                             csub[csub.length] = x;                         }                     }                     return csub;                 }             }         }         return null;     };     function matchSubset(s, sub) {         if (!options.matchCase) s = s.toLowerCase();         var i = s.indexOf(sub);         if (i == -1) return false;         //return i == 0 || options.matchContains;         return true;     };     this.flushCache = function() {         flushCache();     };     this.setExtraParams = function(p) {         options.extraParams = p;     };     this.findValue = function(){         var q = $input.val();         if (!options.matchCase) q = q.toLowerCase();         var data = options.cacheLength ? loadFromCache(q) : null;         if (data) {             findValueCallback(q, data);         } else if( (typeof options.url == "string") && (options.url.length > 0) ){             $.get(makeUrl(q), function(data) {                 data = parseData(data)                 addToCache(q, data);                 findValueCallback(q, data);             });         } else {             // no matches             findValueCallback(q, null);         }     }     function findValueCallback(q, data){         if (data) $input.removeClass(options.loadingClass);         var num = (data) ? data.length : 0;         var li = null;         for (var i=0; i < num; i++) {             var row = data[i];             if( row[0].toLowerCase() == q.toLowerCase() ){                 li = document.createElement("li");                 if (options.formatItem) {                     li.innerHTML = options.formatItem(row, i, num);                     li.selectValue = row[0];                 } else {                     li.innerHTML = row[0];                     li.selectValue = row[0];                 }                 var extra = null;                 if( row.length > 1 ){                     extra = [];                     for (var j=1; j < row.length; j++) {                         extra[extra.length] = row[j];                     }                 }                 li.extra = extra;             }         }         if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);     }     function addToCache(q, data) {         if (!data || !q || !options.cacheLength) return;         if (!cache.length || cache.length > options.cacheLength) {             flushCache();             cache.length++;         } else if (!cache[q]) {             cache.length++;         }         cache.data[q] = data;     };     function findPos(obj) {         var curleft = obj.offsetLeft || 0;         var curtop = obj.offsetTop || 0;         while (obj = obj.offsetParent) {             curleft += obj.offsetLeft             curtop += obj.offsetTop         }         return {x:curleft,y:curtop};     } } jQuery.fn.autocomplete = function(url, options, data) {     // Make sure options exists     options = options || {};     // Set url as option     options.url = url;     // set some bulk local data     options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;     // Set default values for required options     options.inputClass = options.inputClass || "ac_input";     options.resultsClass = options.resultsClass || "ac_results";     options.lineSeparator = options.lineSeparator || "\n";     options.cellSeparator = options.cellSeparator || "|";     options.minChars = options.minChars || 1;     options.delay = options.delay || 400;     options.matchCase = options.matchCase || 0;     options.matchSubset = options.matchSubset || 1;     options.matchContains = options.matchContains || 0;     options.cacheLength = options.cacheLength || 1;     options.mustMatch = options.mustMatch || 0;     options.extraParams = options.extraParams || {};     options.loadingClass = options.loadingClass || "ac_loading";     options.selectFirst = options.selectFirst || false;     options.selectOnly = options.selectOnly || false;     options.maxItemsToShow = options.maxItemsToShow || -1;     options.autoFill = options.autoFill || false;     options.width = parseInt(options.width, 10) || 0;     this.each(function() {         var input = this;         new jQuery.autocomplete(input, options);     });     // Don't break the chain     return this; } jQuery.fn.autocompleteArray = function(data, options) {     return this.autocomplete(null, options, data); } jQuery.fn.indexOf = function(e){     for( var i=0; i<this.length; i++ ){         if( this[i] == e ) return i;     }     return -1; };Only thing is, if your using the matchContains options, it'll probably no longer work. Quote Link to comment Share on other sites More sharing options...
fruitbat Posted December 7, 2008 Author Report Share Posted December 7, 2008 Top Man!I was just about to reply as there was a snippet of code which was auto populating the first match as the 'assumed best match', so typing 'Ab' to hopefully find the result 'Town of Aberdeen' and this was overtyping the text I entered after just 1 key press.BUT The code you have just posted does the job perfectly, thank you very much.Nice way to spend a couple of hours on a SundayJust out of curiosity, what did you change and why. Like to know so I can play and understand, works well for me when learning.Thanks againFrooty Quote Link to comment Share on other sites More sharing options...
Village Idiot Posted December 7, 2008 Report Share Posted December 7, 2008 You've pretty much sussed it yourself from what you said - the autopopulating was screwing things up. I changed this// fills in the input box w/the first match (assumed to be the best match)     function autoFill(sValue){         // if the last user key pressed was backspace, don't autofill         if( lastKeyPressCode != 8 ){             // fill in the value (keep the case the user has typed)             $input.val($input.val() + sValue.substring(prev.length));             // select the portion of the value not typed by the user (so the next character will erase)             createSelection(prev.length, sValue.length);         }     } to // fills in the input box w/the first match (assumed to be the best match)     function autoFill(sValue){         // if the last user key pressed was backspace, don't autofill         if( lastKeyPressCode != 8 ){             //$input.val(sValue);             // select the portion of the value not typed by the user (so the next character will erase)             //createSelection(prev.length, sValue.length);         }     }; I should've removed these lines, but was lazy and just commented it out instead . What it does is stop the text field from getting automatically populated with the closest match - this was causing problems narrowing down the results when you typed the next character. and this function matchSubset(s, sub) {         if (!options.matchCase) s = s.toLowerCase();         var i = s.indexOf(sub);         if (i == -1) return false;         return i == 0 || options.matchContains;     }; to function matchSubset(s, sub) {         if (!options.matchCase) s = s.toLowerCase();         var i = s.indexOf(sub);         if (i == -1) return false;         //return i == 0 || options.matchContains;         return true;     };This is called everytime you type a 2nd/3rd/4th letter and it narrows down the original list of matches that were shown. Before it was only returning true where i = 0, (i.e. if "ab" was a substring of "aberdeen" but not if "de" was a substring of "aberdeen", as i would be 4 in that case. Now it returns true if there's a match anywhere in the string. Quote Link to comment Share on other sites More sharing options...
hI-OOPS-CAPS Posted December 7, 2008 Report Share Posted December 7, 2008 nice one ian !!! i need some bloody time off from work and uni and get to hp sometime Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.