Calculating Personality Quizzes with Gravity Forms

For a recent project I had to determine the most commonly selected answer/value in a quiz submission. This quiz was built using Gravity Forms, so I added a custom merge tag that calculates the most common value in the submission array. This is useful for Buzzfeed-style personality quizzes, example: if you selected mostly ‘pony’ value answers you are a pony.

Once this information is available as a merge tag you can display conditional badges for each result.

How to Create an AJAX powered Sortable Table in WordPress

I recently had to create a sortable and filtered AJAX post list in WordPress. I learned a lot, so I figured I’d post how I did it in hopes of helping someone else!

To get started using AJAX in WordPress I followed this excellent tutorial, “Getting Loopy – Ajax Powered Loops with jQuery and WordPress”, but it uses AJAX to create an infinite scroll. For our example below we are going to be sorting posts by title and rank, and also adding the ability to filter by color. Rank and color are added to the post as custom meta.

Step 1

First we’ll set up our main loop with a table and add pagination. The table header contains a select form element listing all the available colors to choose from.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<table class="index">
  <thead>
    <tr>
      <th class="rank selected"><a id="rank-sort" class="DESC" href="?orderby=rank&order=ASC">Score</a></th>
      <th class="title"><a id="title-sort" href="?orderby=title&order=ASC">Title</a></th>
      <th class="color">
        <form class="custom">
          <select id="customDropdown">
          <option>All colors</option>
          <?php 
          $colors = $wpdb->get_col("SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = 'color'" );
          $colors = array_unique($colors);
          sort($colors);
          foreach ($colors as $val) {
            echo "<option value='" . $val . "'>" . $val . "</option>";
          }
          ?>
          </select>
        </form>
      </th>
    </tr>
  </thead>
  <tbody class="tbody">
  <?php while ( have_posts() ) : the_post(); ?>
    <tr>
      <td class="rank"><?php echo get_metadata( 'post', get_the_ID(), 'rank', true );?></td>
      <td class="title"><a class="title" href="<?php the_permalink(); ?>"><?php the_title(); ?></a></td>
      <td class="state"><?php echo get_metadata( 'post', get_the_ID(), 'color', true ); ?></td>
    </tr>
  <?php endwhile; ?>
    <tr>
      <td colspan="5">
        <div class="pagination">
        <?php 
        echo $wp_query->paged;
        echo paginate_links(array(  
          'base'     => '%_%',  
          'format'   => '/page/%#%',
          'show_all' => true,
          'current'  => max( 1, get_query_var('paged') ),  
          'total'    => $wp_query->max_num_pages
        ));  ?>
        </div>
      </td>
    </tr>
  </tbody>
</table>

Step 2

In our functions.php file we’ll modify the homepage query to sort by rank by default, showing 10 posts at a time.

1
2
3
4
5
6
7
8
9
10
add_action( 'pre_get_posts', 'custom_modify_home' );
function custom_modify_home( $query ) {
  if ($query->is_front_page() && $query->is_main_query()) {
    $query->set('post_status', 'publish');
    $query->set('meta_key','rank');
    $query->set('orderby','meta_value_num');
    $query->set('order','DESC');
    $query->set('posts_per_page', 10);
  }
}

Step 3

Then we’ll create a javascript file containing the AJAX call to handle the sorting and filtering. I called the file ajaxLoop.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
jQuery(function($){
  var sortRank = '0';
  var sortTitle = '0';
  var selectedColor = '0';
  var page = 1;
  var loading = true;
  var $content = $("body .tbody");
  var load_posts = function(){
    $.ajax({
      type : "GET",
      data : {action: "custom_loop_handler", "rank": sortRank, "title": sortTitle, "color": selectedColor, "pageNumber": page},
      dataType : "html",
      url : myAjax.ajaxurl,
      success : function(data){
        $("body .tbody").html(data);
        $('.ajax-paginate a').click(function() {
          if ($(this).hasClass('prev')) {
                        page--;
          } else if ($(this).hasClass('next')) {
            page++;
          } else {
            page = $(this).text();
          }
          load_posts();
          return false;
        });
      },
      error : function(jqXHR, textStatus, errorThrown) {
        alert(jqXHR + " :: " + textStatus + " :: " + errorThrown);
      }
    });
  }
});

This actually doesn’t do anything on it’s own, we need to add event handlers to initiate load_posts() and pass the correct preferences along.

Step 4

We add the javascript below to ajaxLoop.js to pass the user’s choices to our AJAX request and tell it to replace the content in <tbody>. This code also adds ‘selected’ classes to the table headers so we can show the user which column they’re currently sorting/filtering by.

Color Dropdown:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$('#customDropdown').change(function() {
  // Get the selected choice
  selectedColor = $(this).attr('value');
 
  // If one of our other filters had a setting, get and pass along w/ color choice change
  if (!$('#title-sort').parents('th').hasClass('selected')) {
    sortTitle = '0';
  } else {
    sortTitle = $('#title-sort').attr("class");
  }
 
  if (!$('#rank-sort').parents('th').hasClass('selected')) {
    sortRank = '0';
  } else {
    sortRank = $('#rank-sort').attr("class");
  }
 
  // Reset page number to 1
  page = 1;
  load_posts();
  // This is the first choice, hide standard pagination (replacing w/ AJAX powered pagination)
  $('.pagination').hide();
    return false;
});

Title:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$('#title-sort').click(function() {
  // Reset other sort
  sortRank = '0';
  // This selector is now active, highlight table cell
  $(this).parents('th').addClass('selected').siblings('th').removeClass('selected');
 
  // On first click this won't have an asc or desc class to get, so we need to add it
  sortTitle = $(this).attr("class");
  if (!sortTitle) {
    sortTitle = 'ASC';
    $(this).addClass('ASC');
  } else {
    // Change sort class
    $(this).toggleClass('ASC DESC');
  }
 
  // Reset page number to 1
  page = 1;
  // Store sort class for ajax
  sortTitle = $(this).attr("class");
 
  load_posts();
  // Just in case this is the first choice, hide standard pagination (replacing w/ ajax)
  $('.pagination').hide();
  return false;
});

Rank:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$('#rank-sort').click(function() {
  // Reset other sort
  sortTitle = '0';
 
  // This selector is no longer inactive, highlight table cell
  $(this).parents('th').addClass('selected').siblings('th').removeClass('selected');
 
  // Reset page number to 1
  page = 1;
 
  // Change sort class
  $(this).toggleClass('ASC DESC');
 
  // Store sort class for ajax
  sortRank = $(this).attr("class");
 
  load_posts();
  // Just in case this is the first choice, hide standard pagination (replacing w/ ajax)
  $('.pagination').hide();
 
  return false;
});

Pagination:

Our example also includes AJAX powered pagination, which you could optionally remove and just display all results at once. The following handles the pagination link clicks, replacing them with AJAX pagination.

1
2
3
4
5
6
7
8
9
10
11
12
13
// if original pagination link
$('.pagination a').click(function() {
  if ($(this).hasClass('prev')) {
  	page--;
  } if ($(this).hasClass('next')) {
    page++;
  } else {
    page = $(this).text();
  }
  $('.pagination').hide();
  load_posts();
  return false;
});

Step 5

We can add this JS file to WordPress in our functions.php. ‘myAjax’ is called in step 3, line 13 so these need to match!

1
2
3
4
5
6
7
8
add_action('wp_enqueue_scripts', 'register_ajaxLoop_script');
function register_ajaxLoop_script() {
   wp_register_script( 'ajaxLoop', get_stylesheet_directory_uri() . '/js/ajaxLoop.js', array('jquery') );
   wp_localize_script( 'ajaxLoop', 'myAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );        
 
   wp_enqueue_script( 'jquery' );
   wp_enqueue_script( 'ajaxLoop' );   
}

Now that we are passing the choices via AJAX we need to create a function to change the main query and send back new content. This is added to functions.php, you’ll notice the name, custom_loop_handler, is the same as step 3, line 11.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?php
add_action( 'wp_ajax_custom_loop_handler', 'custom_loop_handler' );
add_action( 'wp_ajax_nopriv_custom_loop_handler', 'custom_loop_handler' );
 
function custom_loop_handler() {
  $pageVAR = (isset($_GET['pageNumber'])) ? $_GET['pageNumber'] : 0;
 
  /* IF COLOR IS SET */ 
  if (($_GET['color'] != '0') && ($_GET['color'] != 'All colors')) {
    $colorValue = $_GET['color'];
    $colorQuery = array( array(
      'key'   => 'color',
      'value' => $colorValue
    ));
  } else {
    $colorQuery = array( array(
      'key'     => 'color',
      'compare' => 'EXISTS'
    ));
  }
 
  /* IF TITLE-SORT IS SET */ 
  if ($_GET['title'] != '0') {
    $orderby = 'title';
    $order = $_GET['title'];
  } else {
    $orderby = 'meta_value_num';
    $order = 'DESC';
  }
 
  /* IF RANK-SORT IS SET */
  if ($_GET['rank'] != '0') {
    $orderby = 'meta_value_num';
    $order = $_GET['rank'];
  } else if ($_GET['title'] == '0') {
    $orderby = 'meta_value_num';
    $order = 'DESC';
  }
 
  $my_query = new WP_Query();
  $my_query->query(array(
    'posts_per_page' => 10,
    'orderby'        => $orderby,
    'order'          => $order,	
    'meta_key'       => 'rank',
    'meta_query'     => $colorQuery,
    'paged'          => $pageVAR
  )); 
 
  $totalpages = $my_query->max_num_pages;
 
  /* Prev button is current page +1 (unless the last post displayed is more than the total posts, then don't display) */
  if ($pageVAR >= $totalpages) {
    $prevpage = 0;
  } else {
    $prevpage = $pageVAR + 1;
  }
  /* Next button is current page -1 */
  $nextpage = $pageVAR - 1;
 
  if ($my_query->have_posts()) : while ($my_query->have_posts()) : $my_query->the_post(); ?>
 
  <tr>
    <td class="rank"><?php echo get_metadata( 'post', get_the_ID(), 'rank', true ); ?></td>
    <td class="title"><a class="title" href="<?php the_permalink(); ?>"><?php the_title(); ?></a></td>
    <td class="color"><?php echo get_metadata( 'post', get_the_ID(), 'color', true ); ?></td>
  </tr>
  <?php endwhile; ?>
  <tr>
    <td colspan="5">
    <?php if ($totalpages > 1) { 
      echo '<div class="ajax-paginate">';
      echo paginate_links(array(  
        'base'     => '%_%',  
        'format'   => '/page/%#%',
        'show_all' => true,
        'current'  => $pageVAR,  
        'total'    => $totalpages,  
      )); 
      echo '</div>'; 
    }   ?>
    </td>
  </tr>
  <?php endif;
        die();
  }
?>

If you have any questions or ideas on how to improve, please let me know in the comments!

Download Complete Example Demo
(Updated 10/29/31)

iOS Debugging with Safari

With the (somewhat) recent release of iOS 6 comes the built-in ability to debug your iOS browser using Safari on your mac. Now there are other ways to do this, but the beauty of this method is that if you have a mac running Lion, and an iPhone/iPad running iOS6 – everything you need is already there, no extensions/apps required.

develop-menuStep 1. On your device, visit the Safari settings screen, click Advanced and then turn on Web Inspector.

Step 2. On your mac, go to Safari preferences, click the Advanced tab, and check “Show Develop menu in menu bar.”

Step 3. Connect your device to the mac using a USB cord, then visit a site in your device’s browser.

Step 4. On the mac open up Safari, and click the Develop tab – your device should appear in the dropdown menu. Select the site you’d like to inspect and the web inspector should appear.

inspect-IOS

Learn more: How to view your local WordPress installation on your device »