TextboxList meets Autocompletion

This post discusses a project which has its own page. Please refer to TextboxList for the most up-to-date information.

TextboxList Autocompletion

Demo here

In my previous blogpost I explained how to extend TextboxList to add closing functionality via a link added to each box. But it was missing an important ingredient: autocompletion!

Again, all we have to do is extend the TextboxList class, override some methods, some events, and create some new ones (all prefixed by auto)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
var FacebookList = new Class({
 
  Extends: TextboxList,
 
  data: [],
 
  options: {
    onInputFocus: function() { this.autoShow(); },    
    onInputBlur: function(el) {      
      el.value = ''; 
      this.autoHide();     
    },
    onBoxDispose: function(item) {
      this.autoFeed(item.$attributes.$text);
    },
    autocomplete: {
      'opacity': 0.8,
      'maxresults': 10,
      'minchars': 1
    }
  },
 
  initialize: function(element, autoholder, options) {
    arguments.callee.parent(element, options);
		this.autoholder = $(autoholder).set('opacity', this.options.autocomplete.opacity);
		this.autoresults = this.autoholder.getElement('ul');
		var children = this.autoresults.getElements('li');
    children.each(function(el) { this.add(el.innerHTML); }, this);
  },
 
  autoShow: function(search) {
    this.autoholder.setStyle('display', 'block');
    this.autoholder.getElements('*').setStyle('display', 'none');
    if(! search || ! search.trim() || (! search.length || search.length < this.options.autocomplete.minchars )) 
    {
      this.autoholder.getElement('.default').setStyle('display', 'block');
      this.resultsshown = false;
    } else {
      this.resultsshown = true;
      this.autoresults.setStyle('display', 'block').empty();
      this.data.filter(function(str) { return str ? str.test(search, 'i') : false; }).each(function(result, ti) {
        if(ti >= this.options.autocomplete.maxresults) return;
        var el = new Element('li').set('html', this.autoHighlight(result, search)).inject(this.autoresults);
        el.$attributes.$result = result;
        if(ti == 0) this.autoFocus(el);
      }, this);
    }
  },
 
  autoHighlight: function(html, highlight) {
    return html.replace(new RegExp(highlight, 'gi'), function(match) {
      return '<em>' + match + '</em>';
    });
  },
 
  autoHide: function() {
    this.resultsshown = false;
    this.autoholder.setStyle('display', 'none');    
  },
 
  autoFocus: function(el) {
    if(! el) return;
    if(this.autocurrent) this.autocurrent.removeClass('auto-focus');
    this.autocurrent = el.addClass('auto-focus');
  },
 
  autoMove: function(direction) {    
    if(!this.resultsshown) return;
    this.autoFocus(this.autocurrent['get' + (direction == 'up' ? 'Previous' : 'Next')]());
  },
 
  autoFeed: function(text) {
    if(this.data.indexOf(text) == -1)
      this.data.push(text);    
  },
 
  autoAdd: function(el) {
    if(!el || ! el.$attributes.$result) return;
    this.add(el.$attributes.$result);
    delete this.data[this.data.indexOf(el.$attributes.$result)];
    this.autoHide();
    this.current.$attributes.$input.value = '';
  },
 
  createInput: function(options) {
    var li = arguments.callee.parent(options);
    var input = li.$attributes.$input;
    input.addEvents({
      'keydown': function(e) {
        e = new Event(e);
        this.dosearch = false;
        switch(e.code) {
          case Event.Keys.up: return this.autoMove('up');
          case Event.Keys.down: return this.autoMove('down');        
          case Event.Keys.enter: 
            this.autoAdd(this.autocurrent);
            this.autocurrent = false;
            this.autoenter = true;
            break;
          default: this.dosearch = true;
        }
      }.bind(this),
      'keyup': function() {
        if(this.dosearch) this.autoShow(input.value);
      }.bind(this)
    });
    input.addEvent(Browser.Engine.trident ? 'keydown' : 'keypress', function(e) { 
      if(this.autoenter) new Event(e).stop();
      this.autoenter = false;
    }.bind(this));
    return li;
  },
 
  createBox: function(text, options) {
    var li = arguments.callee.parent(text, options);
    li.addEvents({
      'mouseenter': function() { this.addClass('bit-hover') },
      'mouseleave': function() { this.removeClass('bit-hover') }
    });
    li.adopt(new Element('a', {
      'href': '#',
      'class': 'closebutton',
      'events': {
        'click': function(e) {
          new Event(e).stop();
          if(! this.current) this.focus(this.maininput);
          this.dispose(li);
        }.bind(this)
      }
    }));
    li.$attributes.$text = text;
    return li;
  }
 
});
 
window.addEvent('domready', function() {
  // init
  var tlist2 = new FacebookList('facebook-demo', 'facebook-auto');
 
  // fetch and feed
  new Request.JSON({'url': 'json.html', 'onComplete': function(j) {
    j.each(tlist2.autoFeed, tlist2);
  }}).send();
});

It works by caching all the results from a JSON Request and feeding them to the autocompleter object. When a item is added as a box, it’ removed from the feed array, and when the box is disposed it’s added back, so that it becomes available in the list when the user types.

Another new feature is that you’ll be able to let it add boxes from the HTML directly:

1
2
3
4
5
6
7
8
9
<label>FacebookList input</label>
<input type="text" value="" id="facebook-demo" />
<div id="facebook-auto">
	<div class="default">Type the name of an argentine writer you like</div>
	<ul class="feed">
		<li>Jorge Luis Borges</li>
		<li>Julio Cortazar</li>
	</ul>
</div>

The constructor now takes new parameters to configure the autocompletion, like the minimum number of characters to trigger the dropdown, and more.

Changelog

  • 0.1: initial release
  • 0.2: added click support, removed $attributes use, code cleanup

Download

Click here to download the zip with code and examples

  1. Hi there!

    i want to add the TypeWatch, a plugin that monitors the time between key strokes in a text input box with the textboxlist plugin.

    Any idea how to do it?

  2. dsfsdfs67877 test test

  3. How can I get the Values from that textbox?

  4. Hi, could someone help to add top image-base imaged pointer to the drop-down menu like Mootool Autosuggest has.
    http://www.alainalemany.com/wp-content/uploads/2008/09/autosuggest2.jpg

  5. Great work but I can’t get it work with mootools 1.2

  6. Hey, is there a possibility to just allow the objects in the json array to be intyped (and maybe email-addresses)?

  7. shellmark 3 months ago

    Great script,

    Is there a sample to put 2 input controls in one page, but the drop down values are different?

  8. What you are trying to accomplish would be possible with 2 auto complete boxes… Think about it. If you are making calls to your database script to pull out some data, there is no other way to tell the script to change sql flags and pull something else while typing in the same box.

  9. to make it compatible whit mootools 1.2, some change required
    see http://forum.free.fr/autocomplete/

  10. iie sumitra 4 months ago

    wow….great job, thanks a lot,

    mantap abissss…….top markotop……….

  11. Hi…
    Great script, but i gone crazy…..
    I need to place automatically the cursor inside the text field when u open page….
    anyone know the way to do this?
    tnx

  12. It’s really awesome script. I was really very much impressed with this code.

    Is it possible to take the values from database instead of the html file…

    • You can do it by retrieving from you database the data you need, creating a php variable for example in this form ["var1","var2",...,"varn"] and then writing that in a HTML file. PHP example:
      $fp=fopen(”json.html”,”w+”);
      $i=0;
      $contenido = “[";
      while ($i<$veces){
      $nombre = $listaMiembros[$i]['nombre'];
      $contenido .= “\”$nombre”;
      $apellido = $listaMiembros[$i]['apellido'];
      $contenido .= ” $apellido”;
      $id = $listaMiembros[$i]['id_miembro'];
      $contenido .= ” $id\”";
      if ($i != $veces -1 ){
      $contenido .= “, “;
      }
      $i++;
      }
      $contenido .= “]”;
      fwrite($fp,$contenido);

  13. There are some serious problems with this component: first and foremost, hitting Backspace will first select and highlight the previous tag (this is good), but hitting backspace again will (instead of removing the selected tag as expected), perform a browser Back command, leaving the page and potentially losing all the information entered into the form!

    Secondly, you should really accept auto-complete entries with the Tab key. This is my expected way auto-complete should be performed.

    • Guillermo Rauch 4 months ago

      I hadn’t noticed the backspace bug, what browser are you testing on ?

      And tab for autocomplete is not a standard behavior for autocompleters, let alone a widget made up of various inputs like this.

  14. How do to put nothing in input field at starting ?

    • thomas 4 months ago

      It pass ! sorry my question was untils

  15. Great tool! Is it possible to connect it to a DB to take the values instead of the html? Thx

  16. Ruben Ortiz 6 months ago

    Hello, i am testing in Windows Server 2003 and not working but in Debian Linux this work. Request config special?

    Thank you, great script.

  17. I have tried to get a sql querry to work but I just cant make it work. Cant anyone who got it working please post a example .php file or text?

  18. Hi!

    Nice component, thanks. I have a small problem with it: How can I post values which are not in the autocomplete list?

    Many thanks,
    Atish

  19. Nice component

  20. TechMind 6 months ago

    this is the error im am getting

    j has no properties
    [Break on this error] j.each(tlist2.autoFeed, tlist2);

  21. TechMind 6 months ago

    Hi,

    I downloaded this code and when i execute the page i get javascript error

    // fetch and feed
    new Request.JSON({’url’: ‘json.html’, ‘onComplete’: function(j) {
    j.each(tlist2.autoFeed, tlist2);
    }}).send();

    So i am not able to run the code.. even the demo page URL given also gives thie error message..

    Could you please help..

  22. @Guillermo,

    One small change I would love to see if you decide to commit the time to an update -

    Immediately after the first item is entered, jump the cursor up 50px or so. This would be to prevent the first item from being a different color from the second (because the first item is normally being hovered over, given that the user just clicked on the input)

    Hamy

  23. @myself

    lol - by some of the code listed here - I mean some of the code listed above my post
    ;)

  24. To correctly post / get the data using update

    Note: some of the code listed here is not correct - it has a syntax error causing it to fail.

    Here is what you need to edit -

    Your form needs to have an ID and a NAME. I chose files_form

    Your input also needs and id and a name. I used files.

    If you are not to familiar with JavaScript, then you will need to add a submit button to the form like this:

    Note that there is no need for an onClick

    Now open the file originally titled test.js
    Do a search for tlist2

    You will notice a line something like var tlist2 = new FacebookList(’files’, ‘facebook-auto’);
    That first parameter needs to be the ID/name of your input div

    After you set that accordingly, place the cursor after the send();
    Hit enter a few times to give yourself some room

    Paste in this code
    $(’files_form’).addEvent(’submit’, function () { tlist2.update(); } );

    The $(…) needs to contain the ID/NAME of your form. Note that the line I just pasted ends in a semicolon. The previous examples left that out, resulting in a syntax error and their code not working correctly.

    If you are planning on submitting large amounts of data, then perhaps you should use post instead of get, by changing form method=”post”

    Your data will be stored using the ID/NAME of the input tag. In my case, I can access my data using
    $data = $_POST['files']

    The data is (by default) separated by ###. If you would like to change that, look for this in the textboxlist.js (or the compressed version): separator: ‘###’,

    Hope that helps, and thanks for this awesome resource!

    Hamy

  25. Vey good works but very breakable with mootools 1.2.

    I was using your js with mootools 1.2 with the fix indicated but still with the fix there are some problems.

    When do you think you’ll update your js for mootools 1.2?

    regards, Dario

  26. Hello again,

    i fixed the problem.
    i saved as “json.asp” with encoding utf-8 and i changed all iso-8859-9 to utf-8 back in test.html =)

  27. Hello everyone,

    i want use this, in my turkish project.
    how can i use this with charset=”iso-8859-9″.

    i changed all “utf-8″ to “iso-8859-9″ in test.html but im still seing bad characters.

    Thanks before.

  28. I’m sorry I can’t spell. I meant to say:

    I can’t get the input field data to post anywhere (using PHP or Javascript).

  29. Hello there–

    When I submit the form, where does the information go? I can’t get it to input field data to post anywhere (using PHP and Javascript). I’d like to use this awesome autocomplete feature on a messaging system.

  30. cevarief 7 months ago

    Hi,

    I have a question. i have 3 names picked from autocomplete. John, Audrey and Barret. I close Audrey. When i type A, the autocomplete doesn’t show Audrey anylonger. It seems that every name which has been picked and closed won’t display anymore in the autocomplete.

    Instead of reupdate the json file, is there any function to recall what has been closed ?

    Thanks before.

  31. cevarief 7 months ago

    @ninja
    Thanks for telling how to use update().

    I prefer to use return this.bits.getValues().join(this.options.separator). So the textboxlist.js not bounded to hidden element ($(’MESSAGEToNames’)) unless we put it as function parameter.

    Simply var data = vlist.update(); this will return text with separator defined.

    @itsik
    Thanks for the fix for Mootools 1.2. It works :D

  32. Hi.

    Great script, like it alot!

    But all the options are allready selected, I want to have a empty field and first when you select a “name” it will show up under the searchfield. Can anyone help me with this?

    If do, please do send a mail to me ogkproduction @ gmail . com

  33. @T.Mailrajan
    @ulas

    Just upload the files to a webserver to empty the input content… and rename json.html to json.php or .asp
    after that, you can use all the json data in json.php and clean up the input …

    Thanks to Guillermo Rauch for this script …

  34. Has anyone modified the script (or is there an update coming at all) that will allow JSON objects to be used instead of JSON array? I need to pass back a name value pair, displaying the name but storing the value for posting back to the server.

    Thanks

  35. i need to fill Autocomplete TextBox dynamically while type a character inside that , the textbox is inside one Gridview cell… can anyone help me to resolve ?

  36. Great Script!.
    I was looking for something like this in jQuery, but I didn’t find anything.

    Thanks for sharing!

  37. As In… Gets Data From JSON Link And Uses It

  38. can someone please email the zip file of a working version of “TextboxList meets Autocompletion”

    Sense my email is going in public, I’ll use one I rarely use…
    Mail {@] hankweb {dot] com

    Also, check out my site @ http://hankweb.com — I also have enabled AJAX IM at http://im.hankweb.com and social networking at http://my.hankweb.com

  39. love the script - this is fantastic ..thank you!

  40. Hi, nice script, too bad it doesn’t work for large database item. It crash if database more than 500 items

  41. Herschel Stone 8 months ago

    d76angt0b5341hx4

  42. Hi,

    The script is returning an error if there are more items in fetched.aspx file. There are around 5000 items in JSON format. This error is being fired in Internet Explorer 7.0. It worked well in Chrome.

    Error: Stop Running this script?

    A script on this page is causing Internet Explorer to run slowly. If it continues to run, your computer may become unresponsive.

    Thanks,
    Dheeraj

  43. hi, There are some parameters to that match the first letters of autocomplete?. thanks

  44. Gracias por el codigo exelente

    Saludos
    —————————————-
    http://www.joelcristobal.com

  45. yeahcmoi 8 months ago

    How work the other parts of facebook, like adding a comment ?

  46. If list more than specific number, can it be set to be scroll down, so list is not too long ??

  47. That’s so cool.

  48. how about a facebook footer navigation??? any example for it??

  49. I’ve given up on this for auto-complete functionality. I recommend the Yahoo AJAX library, which is FAR more robust and configurable, with much more user support and documentation provided.

    developer.yahoo.com/yui/autocomplete

  50. Very Nice, thank you very much