Development

Replacing Default WordPress User Dropdowns with an AJAX Solution

WordPress’s default dropdowns for Authors and Parent Pages in WP Admin work well for most setups, but sometimes the large number of users can slow page load and exhaust memory. A solution that I like is replacing the user dropdown with Select2 and use AJAX to load results. Below, I include some code that uses select2 version 3.5.0 and walk you through how to solve this problem.

Replacing the Authors dropdown can be achieved by using the “wp_dropdown_users” filter.

function wds_dropdown_users_callback( $output ) {
    global $post;

    // Is there an author currently set?
    $author_id = isset( $post->post_author ) ? $post->post_author : null;
    $author_data = get_userdata( $author_id );

    // create data to be passed into custom js
    $data = array(
        'post_author' => $author_id,
        'display_name' => isset( $author_data->display_name ) ? $author_data->display_name : null,
        'nonce' => wp_create_nonce( 'wds-replace-user-dd-nonce' ),
        'ajaxurl'   => admin_url( 'admin-ajax.php' ),
        'placeholder_text' => __( 'Select an Author', 'text-domain' ),
    );
            
    // enqueue  css/js
    wp_enqueue_script( 'select2', plugin_dir_url( __FILE__ ) . '/select2-3.5.0/select2.min.js', array('jquery'), '', true );
    wp_enqueue_style( 'select2', plugin_dir_url( __FILE__ ) . '/select2-3.5.0/select2.css', array(), '' );
    wp_enqueue_script( 'wds-replace-user-dropdown', plugin_dir_url( __FILE__ ) . '/wds-replace-user-dropdown.js', array( 'jquery', 'select2' ), '', true );

    wp_localize_script( 'wds-replace-user-dropdown', 'wds_rud_config', $data );
     
    // return custom markup for 
    return '
        <input type="text" name="post_author_override" id="wds-user-search" value="'. $author_id .'"/>
    ';
}

add_filter( 'wp_dropdown_users', wds_dropdown_users_callback );

First, we need to see if there is already a post author set and make sure that data gets passed into our JS. After enqueueing the Select2 JS/CSS and our custom JS, we can output the necessary markup for Select2 to use. Next, we have to setup the AJAX function for Select2 to use. This function will take a string and search for users who match based on email and display name.

function wds_get_users() {
    $security_check_passes = (
        ! empty( $_SERVER['HTTP_X_REQUESTED_WITH'] )
        && 'xmlhttprequest' === strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] )
        && isset( $_GET['nonce'], $_GET['q'] )
        && wp_verify_nonce( $_GET['nonce'],  'wds-replace-user-dd-nonce' )
    );

    if ( ! $security_check_passes ) {
        wp_send_json_error( $_GET );
    }

    // if we have an author id, get the display_name
    if ( isset( $_GET['id'] ) && $_GET['id'] ) {
        $author_data = get_userdata( absint( $_GET['id'] ) );

        $results = array(
            array(
                'id' => $author_data->ID,
                'text' => $author_data->display_name,
            ),
        );

        wp_send_json_success( $results );
    }

    $search = sanitize_text_field( $_GET['q'] );
        
    $user_query = new WP_User_Query( 
        array( 
            'search' => '*'.$search.'*', 
            'search_columns' => array( 'user_login', 'user_email', 'user_nicename', 'ID' ),
            'who' => 'authors',
            'number' => 10,
        ) 
    );

    // bail if we don't have any results
    if ( empty( $user_query->results ) ) {
        wp_send_json_error( $_GET );
    }

    $results  = array();
    foreach ( $user_query->results as $user ) {
        $results[] = array(
            'id' => $user->ID,
            'text' => $user->display_name
        );
    }

    wp_send_json_success( $results );
}

add_action( 'wp_ajax_wds_get_users', 'wds_get_users' );
add_action( 'wp_ajax_nopriv_wds_get_users', 'wds_get_users' );

The first part of this function verifies that this is a valid AJAX request and then checks to see if we received an ID to query against. If there is an ID, we know an author is already set for this post and only lookup the data for that one user to display in the dropdown. Otherwise, we query all users and make sure the entered string matches part or all of the user’s email or display name. We then return the JSON encoded array of all matching users.

Now we have to tell Select2 which field to use (the input from the dropdown_users_callback function) and to use AJAX.

This can be done in Select2’s AJAX option:

        app.$select = $( document.getElementById( 'wds-user-search' ) );

        app.$select.select2({
            placeholder : app.placeholder_text,
            minimumInputLength : 3,
            allowClear: true,
            width: '60%',
            ajax : {
                cache : false,
                url : ajaxurl,
                dataType : 'json',
                data : function (term, page) {
                    return {
                        q : term,
                        action : 'wds_replace_user_dropdown',
                        nonce : app.nonce,
                    };
                },
                results : app.select2Data
            }, ...

The next step is to loop through the AJAX results and return the results in the format Select2 expects.

    app.select2Data = function( ajax_data, page, query ) {
        var items=[];

        $.each( ajax_data.data, function( i, item ) {
            var new_item = {
                'id' : item.id,
                'text' : item.text
            };

            items.push(new_item);
        });

        return { results: items };
    };

Large WordPress sites may run into memory and speed issues when the user dropdown is being built. Using Select2/AJAX is an easy way to avoid performance issues and and allows easier searching for authors. There are other ways to solve this issue, like limiting the number of users in the dropdown, but that could cause other problems (like not being able to find an author). So, I recommend recommend the Select2/AJAX solution because it provides a nice search UI and allows for all authors on a site to be selected from.

Comments

5 thoughts on “Replacing Default WordPress User Dropdowns with an AJAX Solution

  1. There is bug,when we try to use the quick edit setting, then the selectors getting places at the top of the left side of the screen and creating problem…Please fix that…can you please add support for Buddypress selectorbox? I am using BuddyPress Xprofile Custom Fields Type for extending profile fields..But the front end select box are too ugly…like http://imgur.com/a/Bb8MH

    Here is Screenshot of Bug http://imgur.com/a/TadVF

  2. While this does solve the solution of issues with huge select boxes, the user query is still ran before the `wp_dropdown_users` filter is called, so while this does replace it with a select box if you have very large user base, there is still the delay on the initial `get_users` call

Have a comment?

Your email address will not be published. Required fields are marked *

accessibilityadminaggregationanchorarrow-rightattach-iconbackupsblogbookmarksbuddypresscachingcalendarcaret-downcartunifiedcouponcrediblecredit-cardcustommigrationdesigndevecomfriendsgallerygoodgroupsgrowthhostingideasinternationalizationiphoneloyaltymailmaphealthmessagingArtboard 1migrationsmultiple-sourcesmultisitenewsnotificationsperformancephonepluginprofilesresearcharrowscalablescrapingsecuresecureseosharearrowarrowsourcestreamsupporttwitchunifiedupdatesvaultwebsitewordpress