Autocomplete is implemented in Drupal through AJAX. When users type into a textbox, code on the client page dynamically loads new data from the server (a Drupal website) and uses this data to update the user display (provide a drop-down list of matching options, which the user can select from).
Handily, all the mechanics of exchanging data between client and server and of updating the display are handled by the widget, autocomplete.js. Implementing a particular autocomplete textfield requires two parts: (a) a caller (the textfield, with special additions) and (b) a handler, which is a PHP function that parses the request and returns a response.
Prebuilt autocomplete functions
Two autocomplete functions ship with the Drupal core. Each is referenced by an "autocomplete_path"--the uri to which autocomplete requests are sent.
* user_autocomplete()
Use this function to load matching user names. Autocomplete path: user/autocomplete.
* taxonomy_autocomplete()
Use this function to load matching taxonomy terms from a given vocabulary. Autocomplete path: taxonomy/autocomplete.
If one of these matches your needs, then all you need to do is include the special #autocomplete_path selector in a form field. Here's an example for user autocomplete (from comment.module):
$form['admin']['author'] = array(
'#type' => 'textfield',
'#title' => t('Authored by'),
'#size' => 30,
'#maxlength' => 60,
'#autocomplete_path' => 'user/autocomplete',
'#default_value' => $author,
'#weight' => -1,
);
For taxonomy autocomplete, include a vocabulary id, as in this example from taxonomy.module:
$form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield',
'#title' => $vocabulary->name,
'#description' => $help,
'#required' => $vocabulary->required,
'#default_value' => $typed_string,
'#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid,
'#weight' => $vocabulary->weight,
'#maxlength' => 1024,
);
For Drupal 7 the taxonomy_autocomplete function takes as parameter the field_name, which is the name of the term reference field. This is the field name of term reference assigned to entity(i.e. node) to be edited. A quick verification may help by manually making a request via http://site.com/taxonomy/autocomplete/@field_name from a browser. If it returns '[]' then it is working, otherwise. It will provide an error message that can help troubleshoot further.
Example
$form['example'] = array(
'#type' => 'textfield',
'#title' => t('This is the Title'),
'#autocomplete_path' => 'taxonomy/autocomplete/<strong>taxonomy_vocabulary_1</strong>',
'#maxlength' => 30,
);
where taxonomy_vocabulary_1 is the machine name of the taxonomy reference field.
Building a custom autocomplete function
If you want to make your own autocomplete function to answer a need not already met, there are a couple of additional steps.
The form
The form will contain an element such as
$form['city'] = array(
'#type' => 'textfield',
'#title' => 'City',
'#maxlength' => 128,
'#autocomplete_path' => 'cities/autocomplete',
);
Addition to hook_menu
You must add #autocomplete_path to your hook_menu function.
function cities_menu(){
...
// Path with autocomplete function for cities.
$items['cities/autocomplete'] = array(
'title' => 'Autocomplete for cities',
'page callback' => '_cities_autocomplete',
'access arguments' => array('use autocomplete'), //or whatever permission makes sense
'type' => MENU_CALLBACK
);
return $items;
Make a call to the Database, D6 Version
Write the handler function. This will receive an autocomplete request and return data to the client in a form ready to be parsed by automplete.js.
/**
* autocomplete helper
* $string = string for search
*/
function _cities_autocomplete($string) {
$matches = array();
//search table `cities` for cities that begin with the letters the user enters in the form
$result = db_query_range("SELECT city FROM {cities} WHERE LOWER(city) LIKE LOWER('%s%')", $string, 0, 10);
// add matches to $matches
while ($data = db_fetch_object($result)) {
$matches[$data->city] = check_plain($data->city);
}
// return for JS
print drupal_to_js($matches);
exit();
}
Note that you are (a) finding matching records based on user input (b) constructing an array of matches (c) converting the array to JavaScript code and (d) outputting the results.
Also note that we need to escape the city name in the $matches array's values: this is because the values are HTML. Not escaping would open up XSS holes. On the other hand, it also means that you can mark-up the autocomplete suggestions any way you like.
Make a call to the Database, D7 Version
If you on Drupal 7, and want to use the Database API, your code would look slightly different.
/**
* autocomplete helper
* $string = string for search
*/
function _cities_autocomplete($string) {
$matches = array();
$result = db_select('cities', 'c')
->fields('c', array('city'))
->condition('city', '%' . db_like($string) . '%', 'LIKE')
->execute();
// save the query to matches
foreach ($result as $row) {
$matches[$row->city] = check_plain($row->city);
}
// Return the result to the form in json
drupal_json_output($matches);
}
Security note:
Make sure your autocomplete handler has appropriate menu permissions set on it, and respects existing access control mechanisms. Otherwise, you might be exposing sensitive information through the autocomplete.
Adapted from http://drupal.org/node/208424#comment-686252, http://drupal.org/node/42552 and http://timonweb.com/how-create-ajax-autocomplete-textfield-drupal-7
Additional example for D6: http://digitalillusion.altervista.org/wordpress/2009/01/12/drupal-6-link...
Comments
New callback
I've replaced:
print drupal_to_js($matches);
exit();
with:
drupal_json($matches);
with good results.
Reference: http://drupal.org/update/modules/6/7#rename-drupal-to-js
Great Walkthrough
I was hitting a wall about autocomplete and how I couldn't get mine to work.
Then, I found the small glitch.
This didn't work:
This did:
The difference is that the path had to not have a leading slash.
For Drupal 7 code is
For Drupal 7 code is something like this.
where field_tags is taxonomy field in node with widget type autocomplete.
Path should not start with search/...
Do not start your Path with search as in "search/yourlistpath". This will not work because it triggers a Search. Obvious only to those who know ;)
The textfield needs an id.
I was seeing this error in firebug:
I also saw a notice in Drupal referencing line 3707 of forms.inc. This is in Drupal 7.12.
If I add an id to the city field in hook_form, the errors go away...
Thanks arnoldbird
I was having trouble with this for hours. All of the tutorials I encountered have very specific syntax, which I followed.
My autocomplete fields would not get any results until I read this and added the unique ID.
Autocomplete example for custom text field
OMG, thank you! ID is MANDATORY
The #id property appears to be mandatory if you want you user entity reference autocomplete to work at all. Thanks!
Jesus"Uncaught TypeError: Cannot read property 'form' of undefin
For the record, today is Christmas! And thanks to all the truth seeker before me! It's been hell of hours to have had a blade in my teeth to try to figure out wt..
"Uncaught TypeError: Cannot read property 'form' of undefined" which caused by autocomplete used in a custom form which try to render itself, then the #id must be there plus the form['xxx'] must be the same as the #id
$form['edit-keyword'] = array(
'#id' => 'edit-keyword',
'#type' => 'textfield',
'#default_value' => '',
'#autocomplete_path' => 'n-search/autocomplete',
);
Right after adding the #id, it's a merry Xmas all of a sudden!
Not working for anonymous?
I got this working for logged in users but not for anonymous although I had 'access arguments' => ['view published content'] on hook_menu.
So I've changed to 'access callback' => TRUE and it's working now.