www.webdeveloper.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 22

Thread: Simple auto-complete function

Hybrid View

  1. #1
    Join Date
    Aug 2006
    Posts
    2

    Smile Simple auto-complete function

    For everybody who wants a way to create an auto-complete or auto-suggest text box in JavaScript, without use of prototype handlers and other advanced things, here is a sample that works perfectly.

    It gives you an auto-complete like Outlook Express does an auto-complete when you type e-mail address that belongs to Address Book...

    Let's create an sample of a text box that completes e-mail addresses, when you type them.

    First of all, you need to create an array with all values used to auto-complete, in this case, three e-mail addresses, but you can add how many addresses (elements) you need.

    After add all elements, use sort() method to order your array.

    Code:
    var aMail = new Array("albert@mail.com","beth@mail.com","harry@mail.com");
    aMail.sort();
    Now you need to use a standard handler (onKeyUp) of textbox to call the auto-complete function:

    Code:
    <input type="text" name="anyName" onKeyUp="Complete(this, event)">
    Note we call the function "Complete" (showed below) with two arguments. The first is the keyword "this" to pass the textbox object to function, so we don't need to manipulate names or IDs of textboxes and we can work with multiple textboxes on a page). The second parameter is the keyword "event" to pass the properties of onKeyUp event, so we can handle which key was pressed on the keyboard.

    Finally we have the function. As you guess, the name of the function is Complete and here is its code:

    Code:
    function Complete(obj, evt) {
    	if ((!obj) || (!evt) || (aMail.length == 0)) {
    		return;
    	}
    
    	if (obj.value.length == 0) {
    		return;
    	}
    
    	var elm = (obj.setSelectionRange) ? evt.which : evt.keyCode;
    
    	if ((elm < 32) || (elm >= 33 && elm <= 46) || (elm >= 112 && elm <= 123)) {
    		return;
    	}
    
    	var txt = obj.value.replace(/;/gi, ",");
    	elm = txt.split(",");
    	txt = elm.pop();
    	txt = txt.replace(/^\s*/, "");
    
    	if (txt.length == 0) {
    		return;
    	}
    
    	if (obj.createTextRange) {
    		var rng = document.selection.createRange();
    		if (rng.parentElement() == obj) {
    			elm = rng.text;
    			var ini = obj.value.lastIndexOf(elm);
    		}
    	} else if (obj.setSelectionRange) {
    		var ini = obj.selectionStart;
    	}
    
    	for (var i = 0; i < aMail.length; i++) {
    		elm = aMail[i].toString();
    
    		if (elm.toLowerCase().indexOf(txt.toLowerCase()) == 0) {
    			obj.value += elm.substring(txt.length, elm.length);
    			break;
    		}
    	}
    
    	if (obj.createTextRange) {
    		rng = obj.createTextRange();
    		rng.moveStart("character", ini);
    		rng.moveEnd("character", obj.value.length);
    		rng.select();
    	} else if (obj.setSelectionRange) {
    		obj.setSelectionRange(ini, obj.value.length);
    	}
    }
    Done! Everything is finished.

    When you type a text that that begins with letter "a", the rest of mail address "lbert@mail.com" will be completed in textbox.

    So let's look the function.

    The first thing we do is ensure that function have all necessary objects to work.

    The "obj" variable receives the "this" parameter = textbox object.
    The "evt" variable receives the "event" parameter = onKeyUp event properties.
    The "aMail" variable is the array created with elements to auto-suggest.

    If one of these variables is missing we quit the function.

    Code:
    if ((!obj) || (!evt) || (aMail.length == 0)) {
    	return;
    }
    Now, we need to check if textbox have something typed. If not there's nothing to complete, so quit function

    Code:
    if (obj.value.length == 0) {
    	return;
    }
    Now we need to know which key was typed in textbox. This can be done reading the properties of event. In the case of MSIE this property is named "KeyCode" and in the case of Mozilla this property is named "which".

    With the code below, we set the "elm" variable with the right value of the key in accordance with the browser.

    Code:
    var elm = (obj.setSelectionRange) ? evt.which : evt.keyCode;
    We need to evaluate the key code aborting the function in the case of non-text keys pressed (arrows, delete, backspace, etc.).

    Code:
    if ((elm < 32) || (elm >= 33 && elm <= 46) || (elm >= 112 && elm <= 123)) {
    	return;
    }
    By now, we separate all text wrote at textbox, to get the last address of e-mail typed.

    In this case we can separate e-mail addresses by coma (,) or semicolons (.

    Note this code is optional, since we are creating an textbox for mail addresses. This code is to ensure that we always are working with te last address typed in textbox.

    Code:
    // change all semicolons (;) by comas (,)
    var txt = obj.value.replace(/;/gi, ",");
    
    // create an array spliting text by comas
    elm = txt.split(",");
    
    // get the last element of array
    txt = elm.pop();
    
    // remove any spaces of the beginning of the text
    txt = txt.replace(/^\s*/, "");
    
    // ensure the text is greater of zero after removing spaces
    if (txt.length == 0) {
    	return;
    }
    And now, we have to retrive the initial position of the selection in textbox

    This can be done by different ways in MSIE and Mozilla, so we have to detect browser and get the information in accordance with it.

    Code:
    if (obj.createTextRange) {
    	// MSIE
    	var rng = document.selection.createRange();
    	if (rng.parentElement() == obj) {
    		elm = rng.text;
    		var ini = obj.value.lastIndexOf(elm);
    	}
    } else if (obj.setSelectionRange) {
    	// Mozilla
    	var ini = obj.selectionStart;
    }
    After this, we search the array using a for loop, searching the text typed in textbox. In the case of match, we break the loop.

    If a text is found it is appended to the end of text in textbox.

    Code:
    for (var i = 0; i < aMail.length; i++) {
    	elm = aMail[i].toString();
    
    	if (elm.toLowerCase().indexOf(txt.toLowerCase()) == 0) {
    		obj.value += elm.substring(txt.length, elm.length);
    		break;
    	}
    }
    Now, we add a selection to the text appended. This is done by different ways in the case of Mozilla or MSIE:

    Code:
    if (obj.createTextRange) {
    	rng = obj.createTextRange();
    	rng.moveStart("character", ini);
    	rng.moveEnd("character", obj.value.length);
    	rng.select();
    } else if (obj.setSelectionRange) {
    	obj.setSelectionRange(ini, obj.value.length);
    }
    That's all.

    Below is a complete sample of a page with this function and a textbox.

    Code:
    <html>
    <head>
    <title>Test</title>
    
    <script language="javascript" type="text/javascript">
    <!--
    
    var aMail = new Array("albert@mail.com","beth@mail.com","harry@mail.com");
    aMail.sort();
    
    function Complete(obj, evt) {
    	if ((!obj) || (!evt) || (aMail.length == 0)) {
    		return;
    	}
    
    	if (obj.value.length == 0) {
    		return;
    	}
    
    	var elm = (obj.setSelectionRange) ? evt.which : evt.keyCode;
    
    	if ((elm < 32) || (elm >= 33 && elm <= 46) || (elm >= 112 && elm <= 123)) {
    		return;
    	}
    
    	var txt = obj.value.replace(/;/gi, ",");
    	elm = txt.split(",");
    	txt = elm.pop();
    	txt = txt.replace(/^\s*/, "");
    
    	if (txt.length == 0) {
    		return;
    	}
    
    	if (obj.createTextRange) {
    		var rng = document.selection.createRange();
    		if (rng.parentElement() == obj) {
    			elm = rng.text;
    			var ini = obj.value.lastIndexOf(elm);
    		}
    	} else if (obj.setSelectionRange) {
    		var ini = obj.selectionStart;
    	}
    
    	for (var i = 0; i < aMail.length; i++) {
    		elm = aMail[i].toString();
    
    		if (elm.toLowerCase().indexOf(txt.toLowerCase()) == 0) {
    			obj.value += elm.substring(txt.length, elm.length);
    			break;
    		}
    	}
    
    	if (obj.createTextRange) {
    		rng = obj.createTextRange();
    		rng.moveStart("character", ini);
    		rng.moveEnd("character", obj.value.length);
    		rng.select();
    	} else if (obj.setSelectionRange) {
    		obj.setSelectionRange(ini, obj.value.length);
    	}
    }
    
    // -->
    </script>
    
    </head>
    
    <body>
    
    <form name="anyForm">
    <input type="text" name="anyName" onKeyUp="Complete(this, event)">
    </form>
    
    </body>
    </html>
    Ok? It's simple!!! Like JavaScript is...
    Last edited by Ilanio; 08-30-2006 at 06:26 PM.

  2. #2
    Join Date
    Jan 2007
    Posts
    11
    How could you validate this using the same the array? Using onBlur to trigger the validation?

  3. #3
    Join Date
    Dec 2006
    Posts
    267
    Posted on 08-30-2006 and this is his only post. I think it's safe to say he won't be answering your question.

  4. #4
    Join Date
    Jan 2007
    Posts
    11
    so r u offering then? I was just looking for some help. It didnt need to be from the ORIpostr.

    Thanks,
    Shane

  5. #5
    Join Date
    Jul 2004
    Location
    West Coast, Canada
    Posts
    665
    Eh, you could achieve a far more elegant solution if you used the XHR object to inject into a php script's query string, which would contact a database that encompassed all your contacts (email addresses, phone numbers, etc).

    You won't have to loop through the array to check values.
    You won't have to find what keyboard characters you pressed.

    The PHP script would just listen for an input...and the input would be put into the SQL statement--eg:
    PHP Code:
    SELECT personFirstNamepersonLastNamepersonEmailAddress 
    FROM tb_person 
    WHERE personFirstName 
        LIKE 
    '%" . $input . "%' 
    OR personLastName 
        LIKE 
    '%" . $input . "%' 
    OR personEmailAddress 
        LIKE 
    '" . $input . "%'"; 
    This would return a resultset to the PHP script, and you could use PHP 5.2.0's new function, json_encode() to return the resultset in JSON format to the XHR callback object.

    Of course, you'd need to sanitize the input by applying mysql_real_escape_string(); however and ultimately, I see this as a far more elegant solution to an AutoComplete component.

    There are tons of AutoComplete components done in javascript that use the XHR object, like Yahoo's User Interface Library ( http://developer.yahoo.com ).
    Last edited by BuezaWebDev; 01-24-2007 at 05:49 PM.

    "Everything in a web browser."

  6. #6
    Join Date
    Jan 2007
    Posts
    11
    Thanks but that still doesn't solve the validation part of it. Your recommendation is just an alternative to the AutoComplete function. Maybe it's better maybe not...

  7. #7
    Join Date
    Jul 2004
    Location
    West Coast, Canada
    Posts
    665
    Quote Originally Posted by ShaneSwodiddy
    Thanks but that still doesn't solve the validation part of it. Your recommendation is just an alternative to the AutoComplete function. Maybe it's better maybe not...
    "Validation using onBlur"--Can you be more specific? What do you want to validate? Do you want to check if the contact is in your resultset?

    So, this is the use case that I picture:

    [1] User types in "smi", 500 milliseconds later a resultset pops down with all the Smiths
    [2] User chooses "John Smith <john_smith@smith.com>" and sets the textfield value as "john_smith@smith.com".
    [3] User presses the TAB key and actives the onBlur event
    [4] --????????

    What do you see as step 4? What's the scenario that you envision for validation?

    Do you mean to say, if they wanted to try and trick the autocomplete by going back to the field and adding a "d" to the end of it, so it's "john_smith@smith.comd" so that it'll send the email to an invalid address?

    "Everything in a web browser."

  8. #8
    Join Date
    Jan 2007
    Posts
    11
    Yes! And thank you for elaborating on the details. I apologize for not being more specific. your list sum's up what I am trying to do.

    1. The user types in "smi" all the smiths appear.
    2. User chooses John Smith and it sets the text field to "John Smith".
    3. User hits Tab Key and activates onBlur event
    4. Event triggers function to check that "John Smith" is in the array.

    It may seem weird that I want to validate this because john smith is obviously in the array (because his name came up in the list) - but here is why I want to validate it:

    Because this simple auto-complete function (above) works great. But it doesn't prevent someone frome typing something into the form that doesn't match the array, it simply uses the array to "Suggest" a name for them. I want "Lock-down" the form field so that it uses the array to "Suggest" and also to validate. So that they can't type a name into the field that is NOT on the array.

    Thank you so much for your patience and assistance. I apologize for not being this specific.

  9. #9
    Join Date
    Nov 2006
    Posts
    526
    If you want to prevent the user from entering an item not in your list, use a select list. Why would you want to force them to type in the value if it has to be a specific value?

  10. #10
    Join Date
    Jan 2007
    Posts
    11
    Because the list (of names) is over 500+. I don't want to bind a list of 500+ items to a drop-down. Would you?? I want to allow the flexibility of "directory searching" capeabilities but I don't want to allow the user to enter in bogus names cause I later use the name they enter to attach their managers email address to a hidden var before I insert and send emails.

  11. #11
    Join Date
    Oct 2006
    Location
    Ontario Canada
    Posts
    1,160

    -------------

    Gulp.........
    I thought i was starting a new thread.
    Its FrIday. Im tired
    [post removed]
    Drew
    Last edited by cgishack; 01-26-2007 at 07:55 PM.

  12. #12
    Join Date
    Jul 2004
    Location
    West Coast, Canada
    Posts
    665
    To validate for onBlur:

    Code:
    AutoComplete = 
    {
    	resultSet:null,
    	
    	search:function(inputField)
    	{
    		var input = document.getElementById(inputField).value;
    		var scriptURL = "userResults.php?keyword=" + encodeURI(input);
    		
    		YAHOO.util.Connect.asyncRequest('GET', scriptURL, { success:AutoComplete.display, failure:AutoComplete.failedRequest, scope:this});
    	},
    	
    	display:function(o)
    	{
    		//JSON encoded result set
    		var data = eval('(' + o.responseText + ')');
    		
    		
    		// *** IMPORTANT ****
    		// This will allow the validate function to check your data set whether
    		// the input is valid or not.
    		
    		AutoComplete.resultSet = data;
    		
    		var contactList;
    		
    		if (data.length == '0')
    		{
    			return;
    		}
    		else
    		{
    			for (i in data) 
    			{
    				contactList += "<div>" + data[i].firstName + " " + data[i].lastName + "</div>";
    			}
    			
    		}
    		
    		//no more code--but basically this is where you popdown the menu of names
    		
    	},
    	failedRequest:function(o)
    	{
    		alert("Failed request.");
    	},
    	/*
    	
    		Called for onBlur of the text field
    	*/
    	validate:function(inputField)
    	{
    		var input = document.getElementById(inputField).value;
    		
    		for (var i in AutoComplete.resultSet)
    		{
    			//Check if the value is inside our resultset from the database
    			if (input != AutoComplete.resultSet[i].emailAddress)
    			{
    				alert("YOUR INPUT WAS NOT VALID"); //alert the user that they should go back
    				return false;
    			}
    		}
    		
    		return true;
    	}
    }
    In the above example, your input text field should have onBlur='AutoComplete.validate(this);' as an attribute.

    So, this is the updated use-case

    1. User types in smi
    2. Resultset appears with all Smiths
    3. User chooses John Smith <john_smith@hotmail.com>
    4. Text field is set to john_smith@hotmail.com
    5. User presses tab and activates onBlur
    6. AutoComplete's validate function is fired and accepts the value of the textfield (john_smith@hotmail.com)
    7. AutoComplete object will check the completed input value against the resultset's "emailAddress" values
    ---
    SUCCESS CONSTRUCT
    8. User is not prompted and assumes that their input is valid.
    ---
    FAILURE CONSTRUCT
    8. User is notified with an alert() saying that their input is not valid. They must choose a proper value within the resultset. You may also want to reset the focus to the original text field and maybe even clear the old value....that's completely up to you; do whatever you think would make your users more productive.

    "Everything in a web browser."

  13. #13
    Join Date
    Jan 2007
    Posts
    11

    Simple auto-complete function

    BuezaWebDev, thanks for your time in puttting together this code. I didn't expect anyone to just bust something out. Too cool man! But i'm not exactly sure how to incorporate it into my existing app and your USE CASE is slightly off (sorry). To follow up on USE CASE here it is:

    1. The user types in "smi" all the smiths appear.
    2. User chooses John Smith and it sets the text field to "John Smith" (not to john smiths email address).
    3. User hits Tab Key and activates onBlur event
    4. Event triggers function to confirm that "John Smith" is in the "aName" array.

    ** So heres my question, how can I use JUST your validation function (you so gladly put together) in my existing .js file and just have it validate that the name they typed into the form, matches a name from the array.

    BELOW is the code I am working from.


    Code:
    var aName = new Array(
    
    "John Smith",
    "Harry Whodini",
    "Dill Doe",
    "Robyn Hood",
    "Andy Anderson",
    "Jimmy Johnson",
    "Chris Angel",
    "Kevin Mitnick"
    		
    );
    
    
    aName.sort();
    
    function Complete(obj, evt) {
    	 if ((!obj) || (!evt) || (aName.length == 0)) {
     	 	return;
      }
    
      if (obj.value.length == 0) {
      		return;
      }
    
      var elm = (obj.setSelectionRange) ? evt.which : evt.keyCode;
    
      if ((elm < 32) || (elm >= 33 && elm <= 46) || (elm >= 112 && elm <= 123)) {
      		return;
      }
    
      var txt = obj.value.replace(/;/gi, ",");
      elm = txt.split(",");
      txt = elm.pop();
      txt = txt.replace(/^\s*/, "");
    
      if (txt.length == 0) {
      		return;
      }
    
      if (obj.createTextRange) {
       	var rng = document.selection.createRange();
      		if (rng.parentElement() == obj) {
       			elm = rng.text;
      	 		var ini = obj.value.lastIndexOf(elm);
      		}
      } else if (obj.setSelectionRange) {
      		var ini = obj.selectionStart;
      }
    
      for (var i = 0; i < aName.length; i++) {
       	elm = aName[i].toString();
      		if (elm.toLowerCase().indexOf(txt.toLowerCase()) == 0) {
       			obj.value += elm.substring(txt.length, elm.length);
      	 		break;
      		}
      }
    
      if (obj.createTextRange) {
      		rng = obj.createTextRange();
      		rng.moveStart("character", ini);
      		rng.moveEnd("character", obj.value.length);
      		rng.select();
      } else if (obj.setSelectionRange) {
      		obj.setSelectionRange(ini, obj.value.length);
      }
    }
    I want to add a function to the end of this code to validate that the name they typed into the form, matches a name form the array. If there is a match they move to the next formField. If no match is found it alerts them and reset's focus back to the form field.

    Thanks so much for your help. Were so close...

  14. #14
    Join Date
    Apr 2006
    Location
    Houston
    Posts
    1,374
    What am I missing? Isn't this kind of simple?

    Have onBlur="ValidateComplete(this)"; in the input tag

    and then in the ValidateComplete function to just loop through the array comparing values.

    (Warning: I just threw this together without testing - it most likely contains syntax errors)

    HTML Code:
    function ValidateComplete (tag) {
    
     WeCool = false;
     for (i=0;i<aMail.length;i++)
      if (aMail(i) == tag.value) {
       WeCool = true;
      }
     }
    
     if (!WeCool) {
      alert ('Uncool ! Select a valid email value');
      tag.focus;
      tag.select;
     }
    
    }
    Last edited by slaughters; 01-29-2007 at 11:29 AM.

  15. #15
    Join Date
    Jul 2004
    Location
    West Coast, Canada
    Posts
    665
    Hey hey,

    Code:
    function validate(inputField)
    {
    	var input = inputField.value;
    
    	for (var i in aName)
    	{
    		if (input != aName[i].toString())
    		{
    			//Alert the user that they have an invalid input.
    			//Suggestion: use another DIV element to pop up instead of an
    			//alert box, a div would actually be more user-friendly.
    			alert(input.toString() + ' is not a valid input for this field.');
    			
    			 //Resets the focus of the previous text field
    			inputField.focus();
    			
    			//Quits the function as soon as the input does not match what is in
    			//the aName array.
    			return false;
    		}
    	}
    	
    	return true;
    }
    Just add that to the bottom of your .js file.

    For the specified input field, add the onBlur attribute adn set the value to "validate(this);".

    "Everything in a web browser."

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center



Recent Articles