skip to Main Content

I’m trying to figure out a way to reset the selected option of one or more in a series of three dropdowns; this is in a function that reloads a page with certain URL parameters that show the filtered results via the SQL query at the bottom of the function. Explanation and examples below.

enter image description here

There are three dropdowns (Country, City and Delivery) that filter database results returned via URL.

Each filter works in conjunction with the others. I can reset one, two or all three filters by simply reloading example.com/directory/.

But what I really want to do is be able to reset each filter individually and reload the URL with the parameters for the remaining filter(s).

Examples:

If I use the first filter for the country USA, the URL that loads is
example.com/directory/?my_country=USA

If I also filter for Alaska in the second filter, the URL reloads and is
example.com/directory/?my_country=USA&my_state=AK

How can I reset the Country filter and then reload example.com/directory/?my_state=AK ?

Or, to reset State when all three filters are used, i.e.

example.com/program-directory/?my_country=USA&my_state=AK&my_delivery=Classroom

how do I reset State to return

example.com/program-directory/?my_country=USA&my_delivery=Classroom

I can’t use Reset select value to default because I need to reload the specific URL with parameters when a filter is reset.

What’s the best approach?

• Can I exclude the filter being reset from $sql_parts by using PHP?

• Or use jQuery to exclude the reset filter(s) and reload the page with URL parameters of the remaining filters?

• Other possibilities?

Yes, there is a seeming conflict between selecting state and country, as CBroe pointed out, but these three filters are for illustration only, as there are more then three IRL.

PHP:

<div class="filter-level">Country:</div>

<?php
     $country = array(
            'Australia' => 'Australia',
            'China' => 'China',
            'USA' => 'USA',
            // other countries omitted for clarity
        );

    echo '<select id="my_country" name="my_country">';
    echo '<option value="" selected disabled hidden>Select Country</option>';
    foreach ( $country as $key => $value ) {
        $checked_modifier = '';
        if ( ! empty( $_REQUEST['my_country'] ) && $key == $_REQUEST['my_country'] ) {
               $checked_modifier = ' selected';
        }
        echo '<option  value="' . $key . '"' . $checked_modifier . '>'. $value .'</option>';
        }
        echo '</select>';               
    ?>

<div class="filter-button"><input type="submit" class="filter-submit" value="Filter Country"></div>

<div class="reset-filter-button"><input type="submit" class="reset-filter" value="Reset Country"></div> <!-- Reset -->




    <div class="filter-level">State:</div>
    
    <?php
         $state = array(
                'AK' => 'Alaska',
                'AL' => 'Alabama',
                'AB' => 'Alberta',
                 // other states omitted for clarity
            );
    
        echo '<select id="my_state" name="my_state">';
        echo '<option value="" selected disabled hidden>Select State/Province</option>';
        foreach ( $state as $key => $value ) {
            $checked_modifier = '';
            if ( ! empty( $_REQUEST['my_state'] ) && $key == $_REQUEST['my_state'] ) {
                   $checked_modifier = ' selected';
            }
            echo '<option  value="' . $key . '"' . $checked_modifier . '>'. $value .'</option>';
            }
            echo '</select>';               
        ?>
    
    <div class="filter-button"><input type="submit" class="filter-submit" value="Filter State/Province"></div>
    
    <div class="reset-filter-button"><input type="submit" class="reset-filter" value="Reset State"></div> <!-- Reset -->
    
    
    
    <div class="filter-level">Delivery:</div>
    
    <?php
         $delivery = array(
            'Classroom' => 'Classroom Only',
            'Online Only' => 'Online Only',
            'Hybrid' => 'Hybrid',
            );
    
        echo '<select id="my_delivery" name="my_delivery">';
        echo '<option value="" selected disabled hidden>Select Delivery</option>';
        foreach ( $delivery as $key => $value ) {
            $checked_modifier = '';
            if ( ! empty( $_REQUEST['my_delivery'] ) && $key == $_REQUEST['my_delivery'] ) {
                   $checked_modifier = ' selected';
            }
            echo '<option  value="' . $key . '"' . $checked_modifier . '>'. $value .'</option>';
            }
            echo '</select>';               
        ?>
    
    <div class="filter-button"><input type="submit" class="filter-submit" value="Filter Delivery"></div>
    
    <div class="reset-filter-button"><input type="submit" class="reset-filter" value="Reset Delivery"></div> <!-- Reset -->
            </form>
        <?php
    }
}


function directory_widget_filter_sql_parts( $sql_parts, $levels, $s, $pn, $limit, $start, $end, $order_by, $order ) {
    global $wpdb;

    if ( ! empty( $_REQUEST['my_country'] ) ) {
        $sql_parts['JOIN'] .= " LEFT JOIN $wpdb->usermeta um_my_country ON um_my_country.meta_key = 'my_country' AND u.ID = um_my_country.user_id ";
        $sql_parts['WHERE'] .= " AND um_my_country.meta_value = '" . sanitize_text_field( $_REQUEST['my_country'] ) . "' ";
    }

    if ( ! empty( $_REQUEST['my_state'] ) ) {
        $sql_parts['JOIN'] .= " LEFT JOIN $wpdb->usermeta um_my_state ON um_my_state.meta_key = 'my_state' AND u.ID = um_my_state.user_id ";
        $sql_parts['WHERE'] .= " AND um_my_state.meta_value = '" . sanitize_text_field( $_REQUEST['my_state'] ) . "' ";
    }

    if ( ! empty( $_REQUEST['my_delivery'] ) ) {
        $sql_parts['JOIN'] .= " LEFT JOIN $wpdb->usermeta um_my_delivery ON um_my_delivery.meta_key = 'my_delivery' AND u.ID = um_my_delivery.user_id ";
        $sql_parts['WHERE'] .= " AND um_my_delivery.meta_value = '" . sanitize_text_field( $_REQUEST['my_delivery'] ) . "' ";
    }

    return $sql_parts;
}

3

Answers


  1. You could have one Filter button at the end of the form and Reset buttons that set corresponding lists to their empty values, but do not reload the page. This will submit all the filters at once, but if some or all of the lists are reset, their corresponding values will be empty (worst case: example.com/program-directory/?my_country=&my_state=&my_delivery=).

    Something like this:

    <?php
    
    $countries = [
        'Australia' => 'Australia',
        'China' => 'China',
        'USA' => 'USA'
    ];
    
    $states = [
        'AK' => 'Alaska',
        'AL' => 'Alabama',
        'AB' => 'Alberta',
    ];
    
    $deliveries = [
        'Classroom' => 'Classroom Only',
        'Online Only' => 'Online Only',
        'Hybrid' => 'Hybrid',
    ];
    
    function html_select_options(string $label, array $options, string $element, string $current_value): string
    {
        $parts = [
            sprintf('<select id="%1$s" name="%1$s">', htmlspecialchars($element)),
            sprintf('<option value="">%s</option>', htmlspecialchars($label)),
        ];
    
        foreach ($options as $value => $l) {
            $parts[] = sprintf(
                '<option value="%s"%s>%s</option>',
                htmlspecialchars($value),
                $value === $current_value ? ' selected' : '',
                htmlspecialchars($l)
            );
        }
    
        $parts[] = '</select>';
    
        return implode("n", $parts);
    }
    
    function html_button_reset(string $label, string $element): string
    {
        return sprintf(
            '<div class="reset-filter-button"><input type="button" class="reset-filter" value="%s" onclick="%s"></div>',
            htmlspecialchars($label),
            sprintf("document.getElementById('%s').value = '';", htmlspecialchars($element))
        );
    }
    
    function html_button_submit(string $label): string
    {
        return sprintf(
            '<div class="filter-button"><input type="submit" class="filter-submit" value="%s"></div>',
            htmlspecialchars($label)
        );
    }
    
    ?>
    
    <form>
        <div class="filter-level">Country:</div>
        <?=html_select_options('Select Country', $countries, 'my_country', $_REQUEST['my_country'] ?? '') ?>
        <?=html_button_reset('Reset Country', 'my_country') ?>
    
        <div class="filter-level">State:</div>
        <?=html_select_options('Select State/Province', $states, 'my_state', $_REQUEST['my_state'] ?? '') ?>
        <?=html_button_reset('Reset State', 'my_state') ?>
    
        <div class="filter-level">Delivery:</div>
        <?=html_select_options('Select Delivery', $deliveries, 'my_delivery', $_REQUEST['my_delivery'] ?? '') ?>
        <?=html_button_reset('Reset Delivery', 'my_delivery') ?>
    
        <?=html_button_submit('Filter') ?>
    </form>
    
    Login or Signup to reply.
  2. Use js in a similar way to the following:

    <script>
        function resetFilter(filterName) {
            const urlParams = new URLSearchParams(window.location.search);
            urlParams.delete(filterName);
            window.location.href = window.location.pathname + "?" + urlParams.toString();
        }
    </script>
    
    
    
    
    <form action="<?php echo esc_url(home_url('/directory/')); ?>" method="get">
        <div class="filter-level">Country:</div>
        <!-- Your existing Country dropdown code and Add the reset button for Country filter-->
        <div class="reset-filter-button">
            <input type="button" class="reset-filter" value="Reset Country" onclick="resetFilter('my_country')">
        </div>
    
        <div class="filter-level">State:</div>
        <!-- Your existing State dropdown code and Add the reset button for State filter -->
        <div class="reset-filter-button">
            <input type="button" class="reset-filter" value="Reset State" onclick="resetFilter('my_state')">
        </div>
    
        <div class="filter-level">Delivery:</div>
        <!-- Your existing Delivery dropdown code and Add the reset button for Delivery filter-->
        <div class="reset-filter-button">
            <input type="button" class="reset-filter" value="Reset Delivery" onclick="resetFilter('my_delivery')">
        </div>
    
        <!-- Your submit button -->
        <input type="submit" value="Apply Filters">
    </form>
    

    Then handle reset filter in PHP:

    function directory_widget_filter_sql_parts( $sql_parts, $levels, $s, $pn, $limit, $start, $end, $order_by, $order ) {
        global $wpdb;
    
        if ( ! empty( $_REQUEST['my_country'] ) ) {
            // Filtering by 'my_country'
        }
    
        if ( ! empty( $_REQUEST['my_state'] ) ) {
            // For filtering by 'my_state'
        }
    
        if ( ! empty( $_REQUEST['my_delivery'] ) ) {
            // Filtering by 'my_delivery'
        }
    
        return $sql_parts;
    }
    
    Login or Signup to reply.
  3. Introduction

    Any change that we may apply here needs to be compatible with other changes. I will create separate sections for the solution, but note, the codes I’m having are untested, so it’s possible that there will be typos or minor mistakes on my part, so please focus on the idea and be patient with any mistakes that I may make. I’m happy to fix them if I get constructive criticism in the comment-section. I also welcome down-votes as long as they are paired with a comment that explain what’s wrong and allow me to improve my answer.

    Make your select handling abstract

    You have the same code being applied thrice in the example, so if you are to implement a fundamental change, you will always need to apply it thrice. I strongly advise you to make a function out of it to handle the common pattern and the differences would be handled based on parameters:

    function getSelect($name, $options) {
        $template = '<div class="filter-level">' . ucfirst($name) . ':</div>';
    
        $myItem = 'my_' . $name;
    
        $template .= '<select id="' . $myItem . '" name="' . $myItem . '">';
        $template .= '<option value="" selected disabled hidden>Select ' . ucfirst($name) . '</option>';
        foreach ( $options as $key => $value ) {
            $checked_modifier = '';
            if ( ! empty( $_REQUEST[$myItem] ) && $key == $_REQUEST[$myItem] ) {
                   $checked_modifier = ' selected';
            }
            $template .= '<option  value="' . $key . '"' . $checked_modifier . '>'. $value .'</option>';
            }
            $template .= '</select>';               
    
        $template .= '<div class="filter-button"><input type="submit" class="filter-submit" value="Filter Country"></div>';
    
        $template .= '<div class="reset-filter-button"><input type="submit" class="reset-filter" value="Reset ' . ucfirst($name) . '"></div> <!-- Reset -->'
        return $template;
    }
    

    Note that instead of echoing the template right away, I’m generating a template and returning it, so, when you use the function, you will be able to echo it right away.

    Usage of your function

    echo getSelect('country', [
        'Australia' => 'Australia',
        'China' => 'China',
        'USA' => 'USA',
    ]);
    

    or you can embed it into further templates, like

    $selectParams = [
        'country', [
            'Australia' => 'Australia',
            'China' => 'China',
            'USA' => 'USA',
        ],
        'state', [
            'AK' => 'Alaska',
            'AL' => 'Alabama',
            'AB' => 'Alberta',
            // other states omitted for clarity
        ],
        'delivery', [
            'Classroom' => 'Classroom Only',
            'Online Only' => 'Online Only',
            'Hybrid' => 'Hybrid',
        ],
    ];
    $template = '';
    foreach ($selectParams as $name => $options) $template .= getSelect($name, $options);
    

    function for SQL parts

    You are repeating your logic for SQL parts as well. Let’s make a function of it:

    function getSelectSQLParts($wpdb, $name) {
        $sql_parts = [
            'JOIN' => '',
            'WHERE' => '',
        ];
        if ( ! empty( $_REQUEST[$name] ) ) {
            $sql_parts['JOIN'] .= " LEFT JOIN $wpdb->usermeta um_{$name} ON um_{$name}.meta_key = '{$name}' AND u.ID = um_{$name}.user_id ";
            $sql_parts['WHERE'] .= " AND um_{$name}.meta_value = '" . sanitize_text_field( $_REQUEST[$name] ) . "' ";
        }
        return $sql_parts;
    }
    

    Again, if you have to perform fundamental changes, you will have a largely easy time doing so.

    Simplifying left join generation

    function directory_widget_filter_sql_parts( $sql_parts, $levels, $s, $pn, $limit, $start, $end, $order_by, $order ) {
        global $wpdb;
    
        foreach ($levels as $level) {
            $currentResult = getSelectSQLParts($wpdb, $level);
            $sql_parts['JOIN'] .= $currentResult['JOIN'];
            $sql_parts['WHERE'] .= $currentResult['WHERE'];
        }
    
        return $sql_parts;
    }
    

    Here I’m assuming that $levels is an array of strings with the name of the levels, like my_country. If the names are slightly different, then you need to make a conversion of names as well.

    How to make it work properly

    You will need to make sure that these selects, resets and stuff will use the same feature on the client-side rather than repeating it. Either put them all into a form or to create JS infrastructure for it.

    Since you did not even want to have a parameter with default values in the URL, I will assume you want the JS infrastructure here. So make sure you do not send a form, but you do it via Javascript.

    Filter buttons

    let levels = ['my_country', 'my_state', 'my_delivery'];
    for (let select of document.getElementsByClassName("filter-submit") {
        select.addEventListener("click", function() {
            let params = [];
            for (let selected of document.querySelectorAll(levels.map(item => "#" + item).join(","))) {
                if (selected.value) params.push(`${selected.id}=${selected.value}`);
            }
            let finalParams = params.join("&");
            window.location.href = "example.com/directory/" + (finalParams ? `?${finalParams}` : "");
        });
    }
    

    We loop the items having the class of filter-submit and add a click handler to each of them which will go through the select tags having their id among a set we specified and if they have a value of interest, we push it into our params, then convert this array into a string and redirect to the page, either without parameters, if everything was empty, or, redirect to the page along with parameters otherwise.

    Resetting

    for (let reset of document.getElementsByClassName("reset-filter-button")) {
        reset.addEventListener("click", function() {
            let p = this.parentNode;
            p.querySelector("select").value = "";
            p.querySelector(".filter-submit").click();
        });
    }
    

    So, for each reset button, we create a click event handler, which will find their parent and from there, we set the selector inside the parent to have an empty string as a value and click on the filter submit

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search