Musings

ExpressionEngine autocomplete search / jump menu with jQuery

7th October 2009

ExpressionEngine autocomplete search / jump menu with jQuery

This tutorial will show you how to build an text input that will give autocomplete / auto suggestions via AJAX straight from an ExpressionEngine template then redirect straight to the entry. It could be used for searching, or shortcuts to particular entries, I have it working for the latter on a recent project. It will allow you to …

  1. Start typing parts of an entry
  2. Scroll through the autocompleted suggestions that pop up
  3. Click one (or use the keyboard shortcuts)
  4. Get redirected to the relevant entry on the relevant page

I’ve recently altered the functionality recently to use $_POST rather than $_GET which makes it both more secure and easier to implement.

1) Get all the bits you need!

This functionality depends on the following bits, so go get them downloaded and included in your site:

2) The templates

Here’s a screenshot of the templates I’ve used in the demo:

  • The index page is where we’ll house the autocomplete
  • The 2 javascript files will do the work for us
  • The single_entry template is where we’ll redirect to show the full entry
  • The _autocompleter is the template that will dynamically look up the entries as we type them

If you so wish you can download all the templates instead of reading through all the code here.

The index template

Include the Javascript and add a little CSS for the markup, there is some default css included with the autocompleter plugin. Then we’ll need to make the form and that all important input text box:

<h2>Autocomplete</h2> 
<!-- Form action can be anything - make sure it degrades nicely --> 
<form id="quickjump" action="/index.php/autcomplete/results/" method="post"> 
  <fieldset> 
    <p><label for="quickjump_title"></label><br /> 
    <!-- We'll scan this text field for matching entries --> 
    <input type="text" size="20" id="quickjump_title" name="quickjump_title" /></p> 
    <!-- Again the search button is just so it degrades --> 
    <button type="submit">Go</button></p> 
  </fieldset> 
</form>

We’ll then write the JavaScript to call the autocomplete plugin and handle the results

<script type="text/javascript"> 
$(document).ready(function(){ 
  // Autocomplete 
  $("input#quickjump_title").autocomplete("/index.php/autocomplete/_autocompleter/",{ 
    cacheLength: 0, 
    minChars: 3, 
    formatItem: function (row) { return row[0] }, 
    formatResult: function (row){ return row[1] } 
  }).result(function(event, item) { 
    //redirect to the URL in the string 
    window.location.href = "{path="autocomplete/single_entry"}"+item[1]; 
  }); 
}); 
</script>

You’ll notice the autocomplete url we’re calling is one of the templates, it must be referenced with /index.php/template_group/template/ with all slashes intact as well as index.php. The second part of that function will redirect the user to the single entry page with the correct url.

JAVASCRIPT ALTERATIONS

I mentioned I’d altered the script to use $_POST ajax calls rather than sending them in the url with $_GET. This means you can be a bit cleaner with the url you pass as well as enjoying a bit more security (if you enjoy that sort of thing;)

Firstly you’ll need to alter the jquery autocomplete source, lucky for us jQuery have made it really simple – we’ll use the $.ajaxSetup function to ensure we’re posting not getting the results. Download the complete pack and open the uncompressed file, then inside the request function we’re going to set the params for ajax calls just before the $.ajax({ call. At the time of writing that’s at about line 361. Just add the following:

$.ajaxSetup({ type: "POST" });

Make sure it’s before that $.ajax({ call.

You can now remove the /index.php/ part of the request url so it will look like

$("input#quickjump_title").autocomplete("/index.php/autocomplete/_autocompleter/",{ 
...

The _autocompleter template

This template needs to have PHP switched on at the input stage (see the template options)

Pretty simple once we’re in though:

{exp:weblog:entries weblog="thoughts" search:thought_body="&lt;?php echo $_GET['q'] ?&gt;"}
{exp:char_limit total="20"}{exp:html_strip}{thought_body}{/exp:html_strip}{/exp:char_limit}|{url_title}
{/exp:weblog:entries}

I’m using it to access my thoughts weblog, your settings will probably be different, you can adjust as you need. What we’re returning though are pipe separated rows “excerpt|url_title” the autocompleter plugin will do the rest. The PHP is just picking the variable from the url (passed by AJAX) and using the search parameter in the entries tag.

Single entry template

You probably don’t need me to help you here, it’s just a template to pick out the single entry based on the url:

{exp:weblog:entries weblog="thoughts" limit="1"}
<h2>{title}</h2>
{thought_body}
{/exp:weblog:entries}

Simples.

As usual, any questions post them up – I mean to revise and improve this post over time and I’d appreciate your feedback.

Discuss

Get involved in the discussion by using the comment form below.

wil.linssen

8th October 2009

A great tip from Kenny Meyers will ensure the template we use to return the results is viewable by AJAX only http://eeinsider.com/tips/view/ajax-request-code/

Douglas Reed

12th October 2009

Could you use this using the search term log in EE?

wil.linssen

12th October 2009

@Douglas my advice would be to use {query} to update the search term log (only upon successful relocation.) That would mean adding another AJAX call to the redirect callback to another template (or the same one with one conditional captures.)

Something like

userdata[‘screen_name’].”’,’”.$SESS->userdata[‘ip_address’].”’,”.time().”,‘site’,’”.$_GET[‘stuff_from_ajax’].”’”;
$DB->query($sql);
?>

I haven’t checked that, or even used a reference for the global classes, but it’s something like that wink

Mark Bowen

13th October 2009

Hi Wil,

Just wanted to say a massive thank you for this tutorial. Finally got it all working after figuring out why it wasn’t working for me. I was trying this all out on a localhost MAMP (OSX) installation and couldn’t get it to work for ages until I figured out that I needed to supply full URLs such as http://localhost:8888/index.php/ instead of just /index.php/autocomplete/results to the templates.

Once I did that then everything started working! grin

Not too sure why MAMP is different as I’m guessing there wouldn’t be any problem on a true server (haven’t tried that yet but I’m sure it will be fine).

Anyhow a really great tutorial so thanks for that.

Best wishes,

Mark

wil.linssen

13th October 2009

That’s a great point @Mark - I’ve assumed the site is running on the root of the server. The url I have used is relative - handing an absolute would be more full proof.

Polprav

20th October 2009

Hello from Russia!
Can I quote a post in your blog with the link to you?

Wil Linssen

20th October 2009

@Polprav - of course! Thanks for getting in touch.

siddharth

21st October 2009

Help Needed!

I am using a web-method written in a code-behind (c#) to populate my autocomplete fields.

How can I access that data items in the autocomplete options(on client side) so as to make the “suggestions” redirect to a different webpage?

CodeBehind :
<WebMethod()> Public Shared Function GetProjects(ByVal filter As String) As String
      Dim items As New List(Of Item)
      Dim objconn As New SqlConnection(“Data Source=DEV;Integrated Security=SSPI;Initial Catalog=Panacea”)
      objconn.Open()
      Dim Projects As New SqlCommand
      Projects.Connection = objconn
      Projects.CommandType = CommandType.StoredProcedure
      Projects.Parameters.Add(”@strFilter”, SqlDbType.NVarChar).Value = filter
      Projects.CommandText = “hsp_GetProjects”
      Dim datareader As SqlDataReader = Projects.ExecuteReader
      While datareader.Read
        Dim item As Item

        item.value = “http://www.google.com”
        item.name = datareader(“ProjectName”).ToString

        items.Add(item)
      End While
      datareader.Close()
      objconn.Close()
      Dim json As New StringBuilder
      Dim serializer As New JavaScriptSerializer
      serializer.Serialize(items, json)
      Return json.ToString()
  End Function


Aspx file:

//txtProjectName is a text box.
$(”#txtProjectName”).autocomplete(“DJ_AddProject.aspx/GetProjects”, {});

sid s

22nd October 2009

Hi Wil,

I am working on the clickable-autocomplete functionality.

I get my data through a c# web method in the code-behind, in the format

[{“value”: “http://xyz1.com”, “name”:“Project1”},
“value”: “http://xyz2.com”, “name”:“Project2”}]

GetProjects is my webmethod which returns Json serialized string arryas in the aforementioned format.

$(”#ProjectName”).autocomplete(‘Add_Project.aspx/GetProjects ’ , {} );

I do not know how to use formatItem or formatResult in my case, and I have tried pretty much everything but, the returned result does not redirect.

Please help.
I have this delivery tomorrow!
Thanks.
Sid

Wil Linssen

22nd October 2009

@sid the formatting is essentially just returning the pipe separated string as an array.

The jQuery then just tacks on the second item to the ExpressionEngine path (”{path=“autocomplete/single_entry”}”+item[1];) you could just forgoe the formatting and stick the url_title you wanted in there. It’s just nicer to see the title than the url in the search results.

Joe H

31st October 2009

Very nice Wil, thanks for sharing, works great.

Joe H

31st October 2009

Be also cool to search multiple fields in the same weblog, ideas on that?

Wil Linssen

31st October 2009

@Joe yes I’m using Mark Croxton’s Search Fields plugin, it gives a whole lot more power to the search parameter.

Mine looks like:

search:title=”<?php echo $IN->clean_input_data($_GET[‘q’]) ?>” search:Res_Name=”<?php echo $_GET[‘q’] ?>” operator=“OR”

Joe H

31st October 2009

Thanks Wil, I get it now, that was pretty obvious…

Wil Linssen

2nd November 2009

I’ve just updated the tutorial with some instructions to get the autocomplete to work with $_POST rather than $_GET which is safer, easier to implement and just all round a bit better wink

Alexander Jones

27th January 2010

Hey there,
Thanks for the article and code. It is very useful.

One thing to note however is that for this to work properly you MUST have the HTML Strip plugin in your EE installation. To get the plugin you can go here: http://expressionengine.com/downloads/details/html_strip/

This caused me a fair bit of grief thinking my javascript was wrong since I customized a lot. But eventually the problem showed to be that I hadn’t uploaded HTML Strip and once I did, it worked flawlessly.

Thanks again,
-Alex

James

7th February 2010

Hi,

Will this work for EE 2.0?

I’ve got it implemented, and if I type a letter, it seems to list the elements in the code e.g. <head><body> - then lists a few styles - ....

Wil Linssen

7th February 2010

Sounds like you’re getting the user message template. I’d check the template on it’s on (actually visit it in the browser) and check all is well.

Ben

7th February 2010

Hello,

I’m currently using this with EE 2.0 aswell - If I type “Ar” - then i would expect o see companies that are only starting with “Ar” e.g. Argos… But it’s showing all entries and only highlighting the “Argos” entry - I thought it worked by removing entries that do not start with the search query….

Can you assist please?

Ben

21st February 2010

Hi Wil,

Great product you have here.

I’m trying to do a search on the title e.g.

{exp:channel:entries channel=“make” search:title=”<?php echo $_POST[‘q’] ?>”}

The ajax search lists all entries but only highlights the related result/entry - it doesn’t remove all the results that don’t… so I have this massive list…

Any ideas please?

Wil Linssen

22nd February 2010

@Ben - that’s probably to do with the ‘search’ parameters limitations. My advice would be to use Mark Croxton’s search fields plugin - it’ll open up a load more fields you can search (including the title.)

Jordan Moore

23rd March 2010

Hey Wil,

This plugin is fantastc and your instructions made implementing it a breeze.

I want to extend this using Mark Croxton’s search plugin, but say for example I have a news weblog and an events weblog, each with different paths to the single entry page, how will this change the example: “[removed].href = “{path=“autocomplete/single_entry”}”+item[1];
” if there are potentially multiple paths?

Or did I miss something? :S

Many thanks,
Jordan

Shaymaa

18th April 2010

Hi Wil,
Do you happen to know of any alternative or an update for Mark Croxton’s search plugin that works on EE 2? Thanks.

Ben

18th April 2010

Agreed, being able to search title within EE 2.0 using this plugin would be perfecto! smile

Wil Linssen

19th April 2010

I have been tinkering with plans to turn this into an addon, but in the mean time I’d suggest just using the query module in the search results template. That’ll tidy you over for now…

Kurt

27th July 2010

Hi Will,

Thanks for the tutorial! I seem to be getting one problem though…no matter what the search term is, the autocomplete is only displaying the latest entries, instead of the ones that are relevant to the term being searched. Any idea how to fix that?

Kurt

27th July 2010

BTW, I’m still on EE 1.6.8

Leave a comment

Your email is never ever shared, and required fields are marked with *

*

*

Used for Gravatars



Sorry folks, uses the "nofollow" attribute

*


My apologies for this nonsense: please type the word you see below.



Copyright © 2009 Wil Linssen all rights reserved

Wil-Linssen.com is powered by ExpressionEngine: it's good, go and get it.