Implementing Autocomplete jQuery Plugin for Textarea

March 7, 2011

We all know the auto-complete feature. We use it in google search, when google tries to suggest our search term, and you probably saw it in many other sites which implemented the auto-complete for their own use. Most of the auto-complete out there are implemented in a text field, where the user has one line and usually its for a small amount of text.

But what do you do if you want to have auto-complete in a textarea?

This is where things get a little complicated and most websites decided not to implement this feature. Some sites, like Twitter has implemented a simple auto-complete feature for textarea. In this implementation they decided  to show the auto-completion outside of the textarea and without dynamic positioning, so the drop down menu is always at the bottom of the text area no matter where you type.

While this implementation does the job, i was hoping to see a more “sexy” auto-complete like i’m used to from windows apps, where the drop down menu appears right next to the current word that the user is typing.

I searched around and couldn’t find anything interesting implemented like i wanted, so i asked a few of my friends and they just said it can’t be done – can’t be done? i said – challenge accepted!

Basic Concept

I decided to implement a jquery plugin to allow auto-complete for textarea. In order to achieve this behavior i decided to create a clone of the textarea,  but instead of using a textarea i will use a div element, where for each line in the textarea i will replaced by a div element, where the line of the cursor will be used as a span so i could get the span width in order to understand where the cursor is. Here is a prototype i built in order to demonstrate the basic concept of the implementation:

While you are typing a text in the text area i’m building a clone of the textarea data on the left column.
Note: This demonstration doesn’t work on Internet explorer (but the plugin support IE in a basic way).

Challenges

When i started to design this concept i faced a few challenges in mind:

  • How to get the cursor position
  • How to clone the textarea taking into consideration all the styling that may apply to it
  • How to deal with line breaking an line wrapping.


I’m going to try and explain briefly how i approached each challenge:

Cursor Position & Line breaking

This is the basic trick behind all of this plugin. First of all i take the cursor position in the text, rather in the screen. This method returns the location of the cursor inside the text , so if the text is “abc” the function will return 3:


function getTextAreaSelectionEnd(ta) {
		 var textArea = ta;//document.getElementById('textarea1');
		 if (document.selection) { //IE
			var bm = document.selection.createRange().getBookmark();
			var sel = textArea.createTextRange();
			sel.moveToBookmark(bm);
			var sleft = textArea.createTextRange();
			sleft.collapse(true);
			sleft.setEndPoint("EndToStart", sel);
			return sleft.text.length + sel.text.length;
		}
		return textArea.selectionEnd;	//ff and chrome
	}

Once i have the cursor position inside of the text i try to build the text inside the clone div. The trick here is to know when a line is breaking. If the user has pressed enter – we just replace “\n” with a <br/> tag. The real problem is if the line is longer than the width of the textarea, its difficult to know where should we cut the text in order to move it to the next line.

In order to do that, i go over all the chars in the string and for each char I have a map of the actual pixels it capture in the screen so i can know the width of  a certain string. This way i can calculate if a line is longer then the width of the textarea and i can know where to break the line just as the textarea do it.

Cloning

This is a fairly easy task – we are just applying styles that we take from the textarea. we place the clone exactly behind the textarea so we won’t see it, but we will have the same offset of the cursor.

That’s it basically. Now lets see how to use the plugin

Using the plugin

The plugin has one function autocomplete which takes one argument with the following attributes:

  • wordCount {Number} The amount of words you want to get from the position of the cursor in order to match it with your auto-complete list. usually this is set to 1.
  • mode {String} could be “inner” or “outter” where inner is the inline mode where the auto-complete display the drop down list according to the cursot position. The outter mode works the same way as the twitter auto-complete works – at the bottom of the textarea. As for now i don’t support the inner mode for IE – so the outter mode is set for IE. ( i can fix that but i don’t have time….)
  • on {Object}
    • query {Function} will be called to query if there is any match for the user input. the function gets two params: text and callback. the text is the number of words you requested, and the callback should be called if you have anything to suggest to the text that was provided. You need to call the callback by providing an array of strings that you suggest. In case there is no match or no suggestion just call the callback with an empty array.
    • selection {Function} This is not implemented yet, but i plan to support a callback of when the user has selected a suggestion from your list.

You can also style the list as you like by setting your own styles in the auto.css file.

Styling the drop down menu

The plugin create a drop down menu to show the suggestion you have found for the current word that the user is typing. In order to style the to fits your needs, lets see the markup of the list:


<ul class="auto-list" style="left: 91px; top: 113px; display: none;">
    <li data-value="daniel"><mark>d</mark>aniel</li>
    <li data-value="david"><mark>d</mark>avid</li>
</ul>

So as you can see from this example, i set the drop down list with a “auto-list” class name, so you can style it however you want.
Here is the CSS i applied for the purpose of the demos:


ul.auto-list{
	display: none;
	position: absolute;
	top: 0px;
	left: 0px;
	border: 1px solid green;
	background-color: #A3DF99;
	padding: 0;
	margin:0;
	list-style:none;
}
ul.auto-list > li:hover,
ul.auto-list > li[data-selected=true]{
	background-color: #236574;
}

ul.auto-list > li{
	border: 1px solid gray;
	cursor: default;
	padding: 2px;

}

mark{
	font-weight: bold;
}

That’s it – it’s that simple.

You can download the plugin here

Demo

Note: You are advised to test these demos in either Firefox or Chrome.
Now, Let’s see some examples:

Please bear in mind that i’ve developed this plugin just as a demonstration that it can be done and i’m sure it has some bugs and improvments (like i suggest in the following section), but the problem is that i don’t know when i’ll have time to do it :)

Improvments

  • I need to support IE also for “inner” mode
  • I need to support horizontal scrolling (now  i assume that there is word wrap)
  • add onUserSelection event , to allow you to get the user selection from the drop down menu
  • allow you to provide HTML as the drop down menu rather than text
Bookmark and Share
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • E-mail this story to a friend!
  • FriendFeed
  • LinkedIn
  • StumbleUpon
  • Twitter

posted in Blog by Amir Harel

 
Powered by Wordpress and MySQL. Theme by openark.org