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. mensaje xx

  2. can u tell me how i can get the selected value in php to insert into database of user selection.
    because the field name is not working for me.
    When i add a Submit button and try to get Date ($_GET/$_POST) empty string i find.
    Can u help me. thanks

  3. como te jodio el flaco que te copio el plugin!!!

  4. Jeisson Delgado 5 months ago

    Quisiera saber si se necesita alguna configuracion especial para que funcione en un servidor LINUX, gracias por cualquier ayuda.

  5. Good Demo!!!

  6. Hi, I am considering paying to this on one of my clients sites but i need it to work in Swedish. Anyone who knows how to add suport for letters: “å”, “ä”, “ö”?

  7. I was looking for an autosuggest style dropdown that could be used in two scenarios: first, for a search box like Google Suggest/Facebook search/OS X Spotlight search; second, for a taglist-style box accepting multiple values. Textboxlist seems a good solution, but I’ve come across a few problems in TextboxList.Autocomplete.js:

    1. When a function was given in options.remote.extraParams, the line data[this.options.remote.param] = search; stopped working (‘data undefined’).

    2. In search() I had to change
    if(this.values.length) this.showResults(search);
    to
    if(this.values) {if(this.values.length) this.showResults(search);}
    to prevent ‘this.values is null’ error when deleting characters from a search phrase to make the currently displayed search results invalid.

    3. Instead of search() being called directly on keypress events, I modified the code to run an intermediate method that only runs search() after a delay, to prevent too many requests to the server for remote requests.

    searchTimeout:false,
    searchIfRightTime: function(bit)
    {
    if(this.searchTimeout) clearTimeout(this.searchTimeout);
    this.searchTimeout = setTimeout(function(){this.search();}.bind(this), 250);
    },

    PS The ‘LOAD PRIOR 50 COMMENTS’ link on this page doesn’t appear to be working (Safari 4)

  8. Chris Morris 9 months ago

    I like how you have the floating clouds on this page…how did you do that?

  9. Looks great, but I cannot get it to work. I am trying to use the jquery version with autocomplete and am having an issue. When I type a few characters in the input field and then click on one of the matches, I get a bug in Firebug that says “m is undefined”. And no selection appears up in my input field. Any ideas as to what might be happening? It feels like I may have initialized the plug-in incorrectly, but I can’t see what’s wrong.

  10. thankl you very good.

  11. You plugin seems pretty sweet, but the test in the download isn’t working. Its throwing a JS error of “j is undefined” from line 170 of test.js It won’t do any auto-complete functionality unless you delete the predefined values and search specifically for them.

  12. If someone has resolvde how to get the box empty and not auto with all this stuff you write in, msg me pls ! I will pay for resolve this problem! Mail @ milan.andjelkovic@hotmail.com

  13. If someone has resolvde how to get the box empty and not auto with all this stuff you write in, msg me pls ! I will pay for resolve this problem!

  14. I have a problem.
    This scripts work fine in my case.
    But, I was wondering. I just need ONE entry(with autocompletion, of course), and not the hability to select a lot of inputs.. .. how can I do it?
    Andy idea?

  15. Tom and jerry about 1 year ago

    no good because have only one text , i want multi text ?
    var tlist1 = new FacebookList(‘userlist1′, ‘user-auto’);
    var tlist2 = new FacebookList(‘userlist2′, ‘user-auto’);….
    only one Request.JOIN …. I see not ok

  16. Увлекательно. Некоторые моменты не знал.

  17. Steven Barnett about 1 year ago

    So I have a database with about 8,000 entries (minimum, this is subject to grow). Naturally I don’t want to have a PHP file read the entire contents of the database and send back all 8,000 items to be cached and used in the auto-suggest. That’s just ridiculous. Rather, I’d prefer to select only those entries which begin with whatever the user has already typed (so if you type “aut”, it will turn up things like “automobile”, “automatic”, “autism”, etc)

    I could do this if instead of sending the data to json.html it were sent to json.php?tag=aut, however I don’t know how to get the content of the input box while it’s still being edited. Help would be appreciated.

  18. 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?

    • How in the world do you get this working with mysql????
      want it to pull data from database not JSON.HTML

      please help!!!!

  19. dsfsdfs67877 test test

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

  21. 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

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

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

  24. Great script,

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

  25. 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.

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

  27. iie sumitra about 1 year ago

    wow….great job, thanks a lot,

    mantap abissss…….top markotop……….

  28. 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

  29. 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);

  30. 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 about 1 year 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.

    • one day ago I had not noticed the backspace bug too, because my page (send new message) is opened in a new tab and thus the browser has no way to back to the previous page.

      after I sent a new message, the script back to the same page with a short sentence notifying the user that message has been sent. Here I re-use the page again by pressing enter in the address bar, now the page has chance to back to the previous page and the backspace bug can happen.

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

    • It pass ! sorry my question was untils

    • hi Thomas, i am facing same problem , please let me know how did you solved it

      Advance thanks
      raki

    • this is th tstin messag thi in th sn thirh owhorihwoihrowhorh oiwhroi

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

  33. Ruben Ortiz about 1 year 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.

  34. 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?

  35. 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

  36. Nice component

  37. this is the error im am getting

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

  38. 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..

  39. @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

  40. @myself

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

  41. 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

    • Million thanks, very useful information and it works perfectly. :)

  42. 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

  43. 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 =)

  44. 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.

  45. 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).

  46. 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.

  47. 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.

  48. @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

    • gerrard8dg 6 months ago

      hey dude, could i please get the fix for mootools 1.2?
      i hope it addresses my problem.. cos i cant get the autocomplete to work with barackslides on the same index file. seems to be some conflict. thanks!

    • Guillermo Rauch 6 months ago

      Hey dude,

      There’s a big message at the top: http://cld.ly/6d1n1w

      This project has moved on beyond this post. Go to Projects > TextboxList.

  49. 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

    • Hi Christian
      I am looking for same thing , just wondering if you have found any solution please .

      If you manage to find one please let me know rikmon1 at gmail dot com

      Regards
      raki

    • Hey guyes.

      I am looking for same thing , just wondering if you have found any solution please .

      If you manage to find one please let me know milan.andjelkovic@hotmail.com

      Regards