Drupal: Overriding Drupal autocompletion to pass more parameters
Posted: - Modified: | drupal, geekUpdate 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.
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
22 comments
David Eads
2011-08-09T21:05:18ZYou 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.
user
2013-04-01T17:32:55Zhi
Sacha Chua
2011-08-10T01:20:56ZYup, 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:42ZThanks. 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...
Sacha Chua
2011-10-16T18:19:46ZWill: Thanks for sharing the missing steps! =)
Coen Pelckmans
2012-03-18T17:02:12ZI 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
added
form['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
Frippuz
2012-07-31T14:27:03ZGreat 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
Shinto
2012-10-19T10:43:27ZGood !!!!! Thanks :)
Claus
2014-02-11T16:54:37ZThanks 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 inDrupal.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 :/
sachac
2014-02-12T22:14:35ZThat's a much better way to do it! =D
LittleDynamo
2014-02-18T12:36:49ZI figured out a way to do this without having to edit Javascript. It involves overriding the textfield theme function. http://stackoverflow.com/qu...
sachac
2014-02-20T03:36:16ZGreat, thanks! =D
rpwils
2014-03-19T12:52:43Zdoes anyone know how to override the url in Drupal.ACDB.prototype for the entire site?
sachac
2014-03-21T18:54:59ZHmm, would you do that in a .js file that you add with drupal_add_js on all pages?
Rylyn
2014-04-01T06:57:19ZSave my time !
Thanks !
Rylyn
2014-04-01T06:59:22ZI found use this way can solute drupal core form item #autocomplete_path can't cross-domain problem.
Thanks !
tim
2014-04-18T02:09:25Zdo you buy chance have a working module that uses this?
sachac
2014-04-20T00:53:46ZSorry, haven't worked with Drupal in a while. =)
dom
2015-02-24T22:03:07ZSearch Autocomplete is the module you need for better autocompletion.
sachac
2015-03-02T02:42:33ZThanks! I haven't used Drupal ages, but this could help other people.
ny ando raharialinera
2015-07-30T13:59:24ZI 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:49ZGreat post, for adding javascript, #attached is the best practice instead of drupal_add_js #Drupal7