When presenting Drupal content with a View you'd sometimes like to filter the content from a list of links. Ajax filtering is already part of the Views module. Our job is to change the default select list into links. This is how it is done.

This article focuses on Drupal 7, if you'd like to see how this is done in Drupal 8, you should read this post. Some may argue that there are other ways, such as the Better Exposed Filters module for instance. My personal experience of trying that is one of being shipwrecked on the harsh rocks of uncompleted and incompatible code on a sea of patches. In theory it works, but not in practice. With this method you should (fingers crossed) be up an running in a few minutes, instead of spending endless hours trying various contrib modules and their patches. This method entails creating a custom module with form alter and some javascript. If that sounds intimidating don't worry – I'll walk you through it.


This is the end result we are looking for. Buttons (links) that filter by taxonomy terms.


This is the default select list from Views (3) for exposed filters.

The View

Before we can begin, we need to create a view. In this example we're using Drupal Core 7.38 and Views 7.x-3.11. Create a View for a content type with taxonomy tags that you would like the user to be able to filter by. Typically this could be content type Article and taxonomy vocabulary Tags. In this example, based on an actual web site in Swedish language, the content type is inspirationsseminarier (english: inspirational workshops) and the taxonomy is called målgrupp (english: target audience).


Create a basic view listing a content type. Choose Show Content of type content type of your choosing. Uncheck Create a page for this example and check Create a block. Remove the default number of items per page. (You could create a page, but for this example we're doing it as a block). When you're done click Continue & edit. This now is your basic view:


This is what the basic view looks like before we add exposed filters and ajax. The next step is to add an exposed fiter that filters by the tags in the taxonomy you want the user to filter by.

The Exposed Filter

Now we're going to add a Filter Criteria. Click the add button next to the Filter Criteria section title in the Views interface.


Step 1/3. Start typing the name of your content types taxonomy term field in the search field to narrow the list of possible fields and then check the checkbox for the field. In this example the field is a taxonomy term field named Målgrupp. Obviously it will be named something else in your case. When you're done – click Apply (all displays).


Step 2/3. Select Dropdown. Click Apply and continue.


Step 3/3. Check Expose this filter to visitors, to allow them to change it. Filter type shold be Single filter. Operator should be is one of. If you'd like to limit the choice you can multiselect certain terms (like in the screenshot) or you can include all terms. If you limit the choices, remember to also tick the box Limit list to selected items. Click Apply (all displays) when you're done.


Now we have our view and our filter. But we still have to enable ajax and configure Exposed forms style. These settings are found in the Advanced section of your View configuration screen. Per default this is collapsed. Open it by clicking the Advanced section label.


These are the two settings in the advanced section that you should configure.[/caption] The first setting we are manipulating is Use AJAX. This should simply be switched from No to Yes. The other setting is the Exposed form Settings where we should check Autosubmit and Hide submit button.


Check the checkboxes for Autosubmit and Hide submit button and then click Apply (all displays).


In your Views preview you should now be able to see the select list and try out filtering your content by term.[/caption] Up to this point what we have done is a standard Views exposed form with AJAX. This is not bad at all, because it means we have all the "machinery" in place that we want and need:

  • we can display the content of our choice, with the presentation of our choice (teaser, fields or something else)
  • we can filter the content by the terms the editor has tagged it with
  • the content gets refreshed with AJAX without reloading the entire document

Now we just need that extra little thing. To not have a select list as filter, but rather text links that we can style in whichever way we'd like. To see your new view you need to place the block on a page. Create a new Basic page and once it is saved take note of it's node id (in this example it is 287). Head over to the blocks admin section admin/structure/block and place the new block View: Exposed filter example in a region on the new page.


In our example the block from our view is placed in the content region of node 287.

The custom module

To change the markup of the exposed filter we need to use form_alter – a coding hook that let's Drupal intercept a form and change it. We want the markup of the form containing the exposed filter to be a unordered list of links instead of a select list. To create your custom module begin by creating a new folder in your modules catalogue. Typically this would be /sites/all/modules. Good practice would also have you differentiate between contrib modules from drupal.org (such as Views) and your own modules by having subfolders in your modules catalogue. Like this sites/all/modules/contrib <-- modules downloaded from Drupal.org sites/all/modules/custom <-- your own custom modules Whatever you do please don't add any modules to the modules folder of the site root. That folder is only meant for Drupal core modules and your site becomes a mess to maintain if you stick other modules into that catalogue. In this example we create a folder named exposedfilter_buttons in sites/all/modules/custom. In this folder we create a new file named exposedfilter_buttons.info with the following code:

name = Exposed Filter Buttons
description = Create Buttons instead of Select box in Views Exposed Filter
package = Views
core = 7.x
dependencies[] = views

Next we create a new file named exposedfilter_button.module. Before we continue we need to find out the form-id of our exposed filter. This is not completely straight forward, but the way you do it is by inspecting the form element.


Inspect the exposed filter form to find its' ID. In this example the ID is views-exposed-form-exposed-filter-example-block.[/caption] In the exposedfilter_buttons.module file we place the following code and make sure that the id on row 7 matches the form id that we just inspected:


function exposedfilter_buttons_form_alter(&$form, &$form_state, $form_id) {
   * Manipulate output of Views Ajax Exposed filter 
  if ($form['#id'] == 'views-exposed-form-exposed-filter-example-block') {
    $form['#attached']['js'] = array(
      drupal_get_path('module', 'exposedfilter_buttons') . '/js/exposedfilter_buttons.js',

    $links = $form['field_kategori_tid']['#options'];
    $newlinks = array();

    foreach ($links as $tid => $term_name) {
      if ($tid == 'All') {
        $newlinks[] = array('data' => '<span class="filter-tab"><a href="" id="' .$tid . '" class="active">' . $term_name . '</a></span>', 'class' => array('pop-filter-label'));
      if (_get_term_depth($tid) == 1) {
        $newlinks[] = array('data' => '<span class="filter-tab"><a href="" id="' .$tid . '">' . $term_name . '</a></span>', 'class' => array('pop-filter-label'));
    $prefix = theme(
      'item_list', array(
      'items' => $newlinks,
      'type' => 'ul',
      'attributes' => array('id' => 'pop-filter-list'),
      'container_id' => 'scope-list-wrapper',

    $form['links'] = array(
      '#type' => 'markup',
      '#value' => $prefix,
      '#markup' => $prefix,

  // extra submit form handle
  $form['#submit'][] = 'exposedfilter_buttons_form_submit';


// handle form submition 
function exposedfilter_buttons_form_submit($form, &$form_state){
    $values = $form_state['values'];

What this code does is using a form_alter hook to override all forms in Drupal However we only want to override the form with the specific id of our exposed filter, hence the if-condition on row 7. When we've established that we're altering the right form we add a custom javascript on row 8-10. The rest of the code iterates through the taxonomy terms and outputs them as an unordered list in HTML. You may notice that code has some css-classes in it which are specific to the site I was building when writing this code: pop-filter-list, filter-tab and scope-list-wrapper. You can name them differently as suits your theming-needs. Next it's time to add the custom javascript file mentioned on row 8 of the above code. Create a new folder in your custom module: sites/all/modules/custom/exposedfilter_buttons/js/ and in it create a javascript file named exposedfilter_buttons.js containing the following code:

(function ($) {
   * Set active class on Views AJAX filter 
   * on selected category
  Drupal.behaviors.exposedfilter_buttons = {
    attach: function(context, settings) {
      $('.filter-tab a').on('click', function(e) {
        // Get ID of clicked item
        var id = $(e.target).attr('id');
        // Set the new value in the SELECT element
        var filter = $('#views-exposed-form-exposed-filter-example-block select[name="field_kategori_tid"]');

        // Unset and then set the active class
        $('.filter-tab a').removeClass('active');

        // Do it! Trigger the select box
        $('#views-exposed-form-exposed-filter-example-block select[name="field_kategori_tid"]').trigger('change');
        $('#views-exposed-form-exposed-filter-example-block button.form-submit').trigger('click');


   * Manipulate HTML for Views AJAX filter
   * on selected category
  jQuery(document).ajaxComplete(function(event, xhr, settings) {

      case "exposed_filter_example":
        var filter_id = $('#views-exposed-form-exposed-filter-example-block select[name="field_kategori_tid"]').find(":selected").val();

        $('.filter-tab a').removeClass('active');
        $('.filter-tab').find('#' + filter_id).addClass('active');



(It is not uncommon to encounter som jQuery version discrepancies when dealing with jQuery in Drupal and that is why the contrib module jQuery Update exists. The settings that worked for me are jQuery 1.8 on both front end and back end.)


Next activate the module in admin/modules, empty the sites cache and then load the page where you placed the block. If everything works your page should now look something like this:


This is approximately what your page should look like. You should be able to click the text links and dynamically filter and AJAX-reload the content. To wrap things up add your own custom theming and css-hide the default select list.

#edit-field-kategori-tid-wrapper { display:none; }

You're done!