Drupal: Overriding Drupal autocompletion to pass more parameters

Posted: - Modified: | drupal, geek
Update 2014-02-19: See LittleDynamo’s comment with a link to this StackOverflow answer.
Update 2014-02-11: See Claus’ comment below for a better way to do this.

Drupal autocompletion is easy – just add #autocomplete_path to a Form API element, set it to something that returns a JSON hash, and off you go.

What if you want to pass form values into your autocompletion function so that you can filter results?

Searching, I found some pages that suggested changing the value in the hidden autocomplete field so that it would go to a different URL. However, that probably doesn’t handle the autocomplete cache. Here’s another way to do it:

Drupal.ACDB.prototype.customSearch = function (searchString) {
    searchString = searchString + "/" + $("#otherfield").val();
    return this.search(searchString);
};

Drupal.jsAC.prototype.populatePopup = function () {
  // Show popup
  if (this.popup) {
    $(this.popup).remove();
  }
  this.selected = false;
  this.popup = document.createElement('div');
  this.popup.id = 'autocomplete';
  this.popup.owner = this;
  $(this.popup).css({
    marginTop: this.input.offsetHeight +'px',
    width: (this.input.offsetWidth - 4) +'px',
    display: 'none'
  });
  $(this.input).before(this.popup);

  // Do search
  this.db.owner = this;
  if (this.input.id == 'edit-your-search-field') {
    this.db.customSearch(this.input.value);
  } else {
    this.db.search(this.input.value);
  }
}

Drupal.behaviors.rebindAutocomplete = function(context) {
    // Unbind the behaviors to prevent multiple search handlers
    $("#edit-your-search-field").unbind('keydown').unbind('keyup').unbind('blur').removeClass('autocomplete-processed');
    // Rebind autocompletion with the new code
    Drupal.behaviors.autocomplete(context);
}

You’ll need to use drupal_add_js to add misc/autocomplete.js before you add the Javascript file for your form.

Hope this helps!

2011-08-08 Mon 19:16

You can view 22 comments or e-mail me at sacha@sachachua.com.

22 comments

You can also override these methods in a JS file you create yourself as long as it is loaded after misc/autocomplete.js. That's a safer and definitely more preferred technique.

Yup, that's what I did - I put the custom code in a Javascript file that I included after misc/autocomplete.js. =)

Will Hartmann

2011-10-12T07:11:42Z

Thanks. This was very helpful, but to get it to work, I also had to copy Drupal.ACDB.prototype.search() from autocomplete.js into my custom JS file and remove the encodeURIComponent(). Otherwise, the / between the first and second argument gets encoded into /.

Also, make sure your custom JS file loads after the autocomplete.js by adding a weight to the drupal_add_js().

$path = drupal_get_path('module', 'my_module');
drupal_add_js($path . '/my_module.js', array('weight' => 20));

See http://api.drupal.org/api/d...

Will: Thanks for sharing the missing steps! =)

Coen Pelckmans

2012-03-18T17:02:12Z

I made a few changes for more flexibility (Version: Drupal 7)

In the custom do search in Drupal.jsAC.prototype.populatePopup
// Do search
this.db.owner = this;
if ($(this.input).attr('custom-autocomplete') == 'autocomplete') {
this.db.customSearch(this.input.value);
} else {
this.db.search(this.input.value);
}

Changed the customSearch function
Drupal.ACDB.prototype.customSearch = function (searchString) {

// loop through all elements with the filter attribute and add them to the string
$.each($("[custom-autocomplete=filter]"), function(i,v) {
var element = $(v);
searchString = element.val() + "/" + searchString;
});

return this.search(searchString);
};

Added a few attributes to the elements in my form

addedform['myFilterElement']['#attributes'] = array( 'custom-autocomplete' => 'filter');
added form['myAutoCompleteElement']['#attributes'] = array( 'custom-autocomplete' => 'autocomplete');

checked if the args are set in the right order
my_autocomplete_path_function(arg1, arg2, arg3, ..., $string) {
}

I also made the change like Hartmann said: removed the encodeURIComponent

Great post Sacha!

If you want to use this to add or replace existing autocomple functionality in a multi value field, you will need a way to separate the form elements containing the custom-autocomplete properties. Without this addition the searches will contain values from ALL the field instances using 'custom-autocomplete' => 'filter'.
You will for example get a search URL like this: orange/orange/orange/apple/gold instead of just apple/gold

I solved it like this:
Add a new #attributes value:

$form_element['#attributes'] = array ('custom-autocomplete' => 'filter',
'custom-autocomplete-index' => $index);

$index can contain anything, just as long as it is different between the field instances. I've used the field delta value which works nicely.

Also add support for this in your jQuery functionality:

First, change the row: this.db.customSearch(this.input.value);
to
this.db.customSearch(this.input.value, index);

Second, change the customSearch function:


Drupal.ACDB.prototype.customSearch = function (searchString, index) {
// loop through all elements with the filter attribute and add them to the string
jQuery.each(jQuery("[custom-autocomplete=filter]"), function(i,v) {

var element = jQuery(v);
// Extra check to manage forms with multiple field deltas
if (element.attr('custom-autocomplete-index') == index) {
searchString = element.val() + "/" + searchString;
}
});

return this.search(searchString);
};

I hope the can be of use to someone.

//Frippuz

Good !!!!! Thanks :)

Thanks for this post, I referred to it several times and it's still helpful two and a half year later :-)

Maybe this helps someone: for a related use case, I overrode the search function in Drupal.ACDB.prototype. By tying the override to the document.ready event I didn't have to ensure that misc/autocomplete.js got included before my code:

jQuery(function () {
if (Drupal && Drupal.ACDB) {
Drupal.ACDB.prototype.search = function (searchString) {
/* ... */
};
}
});

On another note, I found this snippet from your post a little problematic:

$("#edit-your-search-field").unbind('keydown').unbind('keyup').unbind('blur') ...

This unbinds all events, not just those added by autocomplete.js. Even if you control all code on the site, it has the potential to break browser plugins and user scripts. Sorry for criticizing it without offering a solution, but I felt a need to advise against that kind of coding :/

That's a much better way to do it! =D

LittleDynamo

2014-02-18T12:36:49Z

I figured out a way to do this without having to edit Javascript. It involves overriding the textfield theme function. http://stackoverflow.com/qu...

Great, thanks! =D

does anyone know how to override the url in Drupal.ACDB.prototype for the entire site?

Hmm, would you do that in a .js file that you add with drupal_add_js on all pages?

Save my time !
Thanks !

I found use this way can solute drupal core form item #autocomplete_path can't cross-domain problem.

Thanks !

do you buy chance have a working module that uses this?

Sorry, haven't worked with Drupal in a while. =)

Search Autocomplete is the module you need for better autocompletion.

Thanks! I haven't used Drupal ages, but this could help other people.

ny ando raharialinera

2015-07-30T13:59:24Z

I think, this solution is not only for search, it still very usefull in form API and #autocomplete_path, when you need to override some parameter

ny ando raharialinera

2015-07-30T14:02:49Z

Great post, for adding javascript, #attached is the best practice instead of drupal_add_js #Drupal7