Flexible AutoComplete (Suggested Words) Code for Text Input Fields using JavaScript

by filip 14. November 2008 12:32

As I'm sure you've seen on many websites, displaying suggestions while a user types text into a text field has become quite popular. However, I was rather disappointed with the solutions I've found out there. The solutions I've found had any combination of the following problems:

  • No cross-browser support: Obviously, any acceptable solution needs to work across the major browsers.
  • Limited to a single text field per page: The solution needs to be flexible enough to allow multiple text fields on a page to either display the same suggestions, or display a completely different set of suggestions.
  • Ease of use: The solution needs to be simple to use, yet powerful enough to allow the designer to fairly easily modify the way the code works and change the look of the suggestions.

With that in mind, I attempted to improve on an existing library. I started working with this one, but pretty soon I realized that I would need to rewrite basically all the code. Below is a description of the code. In order to make this work, you will need to add the following to the <head> of your HTML page:

<script language="javascript" type="text/javascript" src="autocomplete.js"></script>

The file referenced above can be downloaded here.


It's probably best to see what we're doing, so below is an example of a text field that uses the autocomplete code. It only auto-completes some text that starts with the letter t, so make sure you type that as the first letter.


Suggests words after the user types in a T (ex: two, three )

Assuming you have the autocomple.js file correctly included in the page, the way to populate the autocomple drop down is with the collowing code:

<input type="text" size="40" autoComplete="mygroup1" /> 
<script language="javascript" type="text/javascript"> 
    autoComplete.Set("mygroup1", new Array("two", "three", "twenty", "tweak", "tool", 
"two hundred", "testing", "two-thirds", "terran", "tomato", "tower", "twin", "task",
"toolbar", "test")); </script>

In order to demonstrate that this is flexible enough to support multiple text fields on the same page, here is another example of a text field, along with the code necessary to display it. Also, notice that I'm creating a new "group", which allows me to display different suggestions in the text field. If I wanted to, I could simply assign the same "group", and this second text field would have identical suggestions to the one above.


Suggests words after the user types in a F (ex: four, five )

<input type="text" size="30" autoComplete="mygroup2" /> 
<script language="javascript" type="text/javascript"> 
    autoComplete.Set("mygroup2", new Array("four", "five", "fifty", "fast", "filip", "fun", 
"feast", "farse", "football", "fantasy", "fork", "fanta", "festival", "fall", "foo")); </script>

Obviously, people will want to style the drop downs differently. The autocomplete.js file contains script which registers a default style, but it also allows you to pass in a style using the autoComplete.Set() method. In the example below, I've created a new style, and assigned it by passing the class name of the style as the third parameter into the Set() method.


Suggests words after the user types in a A (ex: apple, art )

<style type="text/css"> 
.newPopupStyle { font-family:Verdana; font-size: 9pt; color:aqua; width: 150px; margin: 3px; 
background-color: Red; font-weight: bold; } .newPopupStyle div { top: -3px; left: -3px; background: brown; border: solid 1px blue;
position: relative; overflow: auto; max-height: 100px; } .newPopupStyle span { cursor: default; margin: 2px; display: block; } .newPopupStyle span.wordSelected { background: yellow; color: Fuchsia; } </style>
<
input type="text" size="30" autoComplete="mygroup3" /> <script language="javascript" type="text/javascript"> autoComplete.Set("mygroup3", new Array("apple", "aspen", "art", "artistic",
"abracadabra"), "newPopupStyle"); </script>

There is more you can do with the code. The javascript file is farily well documented, so hopefully if you need to change anything, it will be pretty straightforward. You can actually greatly reduce the size of the file if you remove the comments.

KNOWN ISSUES:

  1. If the margin of the body tag is not 0, the drop down will not render in the appropriate spot in IE. Apply a margin (ex: <body style="margin: 0;"> ) to fix the problem.

UPDATES:

  1. Feb. 25, 2009 – Fixed a problem in autocomplete.js.  It should now also work on Safari 4.0 beta.

LICENSE:

  • I have received a few questions about the license for this code.  You can use this code for any purpose, including commercial.  If you do find issues or bugs with the code, I do ask that you let me know so that I can put in a fix.

Tags: , ,

Web Development

C# Priority Queue Implementation for Silverlight

by filip 20. October 2008 19:23

While writing some code for Silverlight, I realized that Silverlight does not include a PriorityQueue implementation in their libraries.  I searched around, but I couldn't find any code that I thought was decent, so I wrote up something pretty quick.  I'll be testing this some more in the near future, but for now, I think it has the basics.  This should be fairly quick for large collections, as the enqueue is logarithmic, while the dequeue is pretty much as fast as it can be.

The queue uses generics for both the priority and the data that is being queued. The only thing to keep in mind is that if you're creating your own type for priority, it must implement IComparable.

 

   1: using System;
   2: using System.Collections.Generic; 
   3:  
   4: namespace bloodforge
   5: {
   6:     public class PriorityQueue<P, T>
   7:         where P : System.IComparable<P>
   8:     {
   9:         protected List<Queue<PriorityItem<P, T>>> _queues;
  10:         protected int _count; 
  11:  
  12:         public PriorityQueue()
  13:         {
  14:             _queues = new List<Queue<PriorityItem<P, T>>>();
  15:             _count = 0;
  16:         } 
  17:  
  18:         /// <summary>
  19:         /// Add an item to the priority queue
  20:         /// </summary>
  21:         public void Enqueue(P priority, T data)
  22:         {
  23:             if (_count == 0)
  24:             {
  25:                 Queue<PriorityItem<P, T>> NewQueue = new Queue<PriorityItem<P, T>>();
  26:                 NewQueue.Enqueue(new PriorityItem<P, T>(priority, data));
  27:                 _queues.Add(NewQueue);
  28:             }
  29:             else
  30:             {
  31:                 QueueInsert(priority, data, 0, _queues.Count - 1);
  32:             } 
  33:  
  34:             _count++;
  35:         } 
  36:  
  37:         /// <summary>
  38:         /// Helper method for Enqueue
  39:         /// </summary>
  40:         private void QueueInsert(P priority, T data, int qLo, int qHi)
  41:         {
  42:             if (qLo == qHi)
  43:             {
  44:                 // There is only one item left to compare.
  45:                 // Need to decide where this item belongs in relation to the last item.
  46:                 if (_queues[qLo].Peek().Priority.CompareTo(priority) < 0)
  47:                 {
  48:                     Queue<PriorityItem<P, T>> NewQueue = new Queue<PriorityItem<P, T>>();
  49:                     NewQueue.Enqueue(new PriorityItem<P, T>(priority, data));
  50:                     _queues.Insert(qLo, NewQueue);
  51:                     return;
  52:                 }
  53:                 else if (_queues[qLo].Peek().Priority.CompareTo(priority) > 0)
  54:                 {
  55:                     Queue<PriorityItem<P, T>> NewQueue = new Queue<PriorityItem<P, T>>();
  56:                     NewQueue.Enqueue(new PriorityItem<P, T>(priority, data));
  57:                     _queues.Insert(qLo + 1, NewQueue);
  58:                     return;
  59:                 }
  60:                 else
  61:                 {
  62:                     _queues[qLo].Enqueue(new PriorityItem<P, T>(priority, data));
  63:                     return;
  64:                 }
  65:             }
  66:             else
  67:             {
  68:                 // Get the middle item from the queue and see if we
  69:                 // need to go to the first or second half of the queues list
  70:                 int qMid = Convert.ToInt32(Math.Floor((qLo + qHi) / 2));
  71:                 if (_queues[qMid].Peek().Priority.CompareTo(priority) < 0)
  72:                 {
  73:                     // This item belongs in the upper half of the range
  74:                     QueueInsert(priority, data, qLo, qMid);
  75:                     return;
  76:                 }
  77:                 else if (_queues[qMid].Peek().Priority.CompareTo(priority) > 0)
  78:                 {
  79:                     // This item belongs in the lower half of the range
  80:                     QueueInsert(priority, data, qMid + 1, qHi);
  81:                     return;
  82:                 }
  83:                 else
  84:                 {
  85:                     // we got lucky, the middle item is of the same priority
  86:                     _queues[qMid].Enqueue(new PriorityItem<P, T>(priority, data));
  87:                     return;
  88:                 }
  89:             }
  90:         } 
  91:  
  92:         /// <summary>
  93:         /// Remove the top item from the queue
  94:         /// </summary>
  95:         public T Dequeue()
  96:         {
  97:             if (_queues.Count == 0)
  98:             {
  99:                 // There are no items in the priority queue
 100:                 return default(T);
 101:             } 
 102:  
 103:             // Get the first item from the first queue
 104:             T data = _queues[0].Dequeue().Data; 
 105:  
 106:             if (_queues[0].Count == 0)
 107:             {
 108:                 // If the queue at the top priority is empty, remove it
 109:                 _queues.RemoveAt(0);
 110:             } 
 111:  
 112:             _count--; 
 113:  
 114:             return data; 
 115:  
 116:         } 
 117:  
 118:         /// <summary>
 119:         /// Retrieves the top item from the queue without removing it
 120:         /// </summary>
 121:         public T Peek()
 122:         {
 123:             if (_queues.Count > 0)
 124:             {
 125:                   return _queues[0].Peek().Data;
 126:             }
 127:             else return default(T);
 128:         } 
 129:  
 130:         /// <summary>
 131:         /// Gets the number of items in the priority queue
 132:         /// </summary>
 133:         public int Count
 134:         {
 135:             get
 136:             {
 137:                 return _count;
 138:             }
 139:         } 
 140:  
 141:         /// <summary>
 142:         /// Returns a string representation of the queue
 143:         /// </summary>
 144:         public override string ToString()
 145:         {
 146:             string val = string.Empty;
 147:             foreach(Queue<PriorityItem<P, T>> queue in _queues)
 148:             {
 149:                 PriorityItem<P, T>[] items = queue.ToArray();
 150:                 foreach (PriorityItem<P, T> item in items)
 151:                 {
 152:                     val += string.Format(" [ {0} ] ", item.Data.ToString());
 153:                 }
 154:             }
 155:             return val.TrimEnd(',');
 156:         }
 157:     } 
 158:  
 159:     public class PriorityItem<P, T>
 160:         where P : System.IComparable<P>
 161:     {
 162:         public P Priority;
 163:         public T Data; 
 164:  
 165:         public PriorityItem(P priority, T data)
 166:         {
 167:             this.Priority = priority;
 168:             this.Data = data;
 169:         }
 170:     }
 171: } 

Tags: ,

Web Development

Very nice and flexible file upload control with progress

by filip 18. October 2008 01:03

I needed a pretty quick way of displaying file upload progress in a ASP.NET project. In the past while working for a different employer, I've written my own code from scratch, and it was a complete pain in the ass.  Fortunately, after doing a bit of searching, I've ran across an open source control that works great.  The control is called NeatUpload and can be found at this URL:

http://www.brettle.com/neatupload

Tags:

Web Development

Server Side Includes (SSI) adding a blank/empty line to HTML page

by filip 28. August 2008 17:28

While creating a page that used server side includes, I ran into some very strange behavior.  Right before each SSI, a blank line would appear on the page.  It didn't have anything to do with margins or padding.  It was just like an empty line appeared right out of nowhere, messing up the layout.  First, I thought it was some problem with IIS and SSI, but I couldn't find anything there.

After a few hours of frustration and trying the weirdest solutions I could think of, I started playing around with saving the include files with different file names. Well, when I got to the *.txt, I noticed that Visual Studio gives me a new option under "Save As"... its "Advanced Save Options". When I looked at the encoding of the file, it was set to "Unicode (UTF-8 with signature) - Codepage 65000".  I then switched to "Unicode (UTF-8 without signature) - Codepage 65001", and the blank lines disappeared. 

It appears that Visual Studio picks an encoding when saving pages that doesn't really produce desirable effects when using server side includes.  However, you can go to File -> Advanced Save Options and select a different encoding.  I'm not sure how to set the one w/o signature as the default yet, but hopefully soon I'll figure that out :)

Tags: ,

Web Development

Using LoadVars sendAndLoad instead of WebService when calling web services with Flash ActionScript

by filip 14. August 2008 17:53

I've ran into issues using the WebService object in Flash.  The thing is incredibly buggy.  In certain cases, it works just fine, but in other cases, it flat out does not work. In my last case, the web service loaded perfectly fine (onLoad was called), but the PendingCall would not return anything.  The onFault method was never called after I invoked my web service call, but the web service never returned either.  Examining the outgoing messages from Flash (using HTTP headers in Firefox) revealed that Flash was never even attempting to call the web service.  Flash did correctly acquire the crossdomain.xml file (which allowed access from all domains), and it did load the WSDL.  But, for whatever reason, it did not call the web method like it was supposed to.

Well, after a few hours or attempging to make this work, I gave up with the WebService object.  Instead, I went to the LoadVars object, something I'm more confident in than the WebService class, simply due to the fact that it has been around much longer and has had more attention from the Adobe / Macromedia team.

Using the WebService object, my code looked like the following:

var _wsdl:String = "http://www.bloodforge.com/webservice.asmx?WSDL"; // <- this is fake for the purpose of this blog 
var MailService:WebService = new WebService(_wsdl);
MailService.onLoad = function()
{
  var MailServiceResult:PendingCall = MailService.MyMethod("my parameters");
  MailServiceResult.onFault = function(fault)
  {
    trace("it never gets here even though it fails!");
  }
  MailServiceResult.onResult = function(result)
  {
    trace("it never got here either!");
  }
}

The above was replaced with the code below, and it works perfectly fine:

var result_lv:LoadVars = new LoadVars();
result_lv.onData = function(responseStr:String)
{
  if(responseStr == undefined)
  {
    trace("Error occurred!");
  }
  else
  {
    trace("Success... parse my data below...");
  }
}
var send_lv:LoadVars = new LoadVars();
send_lv.myWebServiceParam1 = "param 1";
send_lv.myWebServiceParam2 = "param 2";
send_lv.sendAndLoad("http://www.bloodforge.com/webservice.asmx/MyMethod", result_lv, "POST");

 
Oh, and make sure that if you do this with your web service, that you have the following line in your web.config file.  Otherwise, you won't be able to access it using these methods (you should be familiar w/ it anyways if you use Ajax to call web services).

<webServices>
  <
protocols
>
    <
add name="HttpGet"
/>
    <
add name="HttpPost"
/>
  </
protocols
>
</
webServices>

Tags: , ,

Web Development

About Filip Stanek

Death Note Pic I'm a developer at ACG in Cincinnati, OH. I like ASP.NET, Flash, and other web technologies, & enjoy playing chess, video games, etc.

Currently playing:
- StarCraft IIE-mail me Send mail

Disqus

Month List