0

Thanks to some wonderful help here at stack exchange, I have built a comment section with a dropdown menu that adds comment_meta to the comments.

I currently have a working filter that uses GET to trigger an if statement to query based on this comment_meta info. The thing I don't like is that it refreshes the page each time you click a button. I'm looking for a way to filter without having the page refresh. I think I need Javascript for this.

I've set up two sets of buttons. The top row is my GET buttons, which work, but refresh the page. The bottom row are my javascript buttons. I can get them to filter the text WITHIN the comments, but I'd like them to filter the entire contents instead.

For instance, if you go to Madonna's page (https://staging3.recordcollectorsoftheworldunite.com/artist/madonna/), and scroll down to the comments, they don't even show up until you press 'show all'. Once you press "Show All" it looks right, but again when you filter, it only filters the comments.

I've added 3 photos to the bottom of this post.

-First photo is when the page loads, how all the comments are hidden- I'd like "ALL" to automatically be selected when the page loads. I tried <button class='button active'> but that must not be the right way to say it.

-Second photo is when "SHOW ALL" is clicked

-Third photo is when "BUY" is clicked.

Here's my comments.php

<?php
comment_form();
if (have_comments()) : ?>
    <div class="container">
        <div class="btn-group flex">
            <button type="button" onclick="window.location.href='<?php echo the_permalink();?>/?cmeta=ALL'">SHOW ALL</button>
            <button type="button" onclick="window.location.href='<?php echo the_permalink();?>/?cmeta=BUY'">BUY</button>
            <button type="button" onclick="window.location.href='<?php echo the_permalink();?>/?cmeta=SELL'">SELL</button>
            <button type="button" onclick="window.location.href='<?php echo the_permalink();?>/?cmeta=TRADE'">TRADE</button>  
            <button type="button" onclick="window.location.href='<?php echo the_permalink();?>/?cmeta=TALK'">TALK</button>  
        </div>  
    </div>
    <hr>
    <div class="container">
        <div class="btn-group flex">
            <button class="button active" onclick="filterSelection('all')"> Show all</button>
            <button class="button" onclick="filterSelection('BUY')"> BUY</button>
            <button class="button" onclick="filterSelection('SELL')"> SELL</button>
            <button class="button" onclick="filterSelection('TRADE')"> TRADE</button>
            <button class="button" onclick="filterSelection('TALK')"> TALK</button>
        </div>  
    </div>
    <style>
    .filterDiv {
    
    display: none;
    }

    .show {
    display: block;
    }

    .container {
    margin-top: 20px;
    overflow: hidden;
    }

    </style>
    <script>
    filterSelection("all")
    function filterSelection(c) {
    var x, i;
    x = document.getElementsByClassName("filterDiv");
    if (c == "all") c = "";
    // Add the "show" class (display:block) to the filtered elements, and remove the "show" class from the elements that are not selected
    for (i = 0; i < x.length; i++) {
        w3RemoveClass(x[i], "show");
        if (x[i].className.indexOf(c) > -1) w3AddClass(x[i], "show");
    }
    }

    // Show filtered elements
    function w3AddClass(element, name) {
    var i, arr1, arr2;
    arr1 = element.className.split(" ");
    arr2 = name.split(" ");
    for (i = 0; i < arr2.length; i++) {
        if (arr1.indexOf(arr2[i]) == -1) {
        element.className += " " + arr2[i];
        }
    }
    }

    // Hide elements that are not selected
    function w3RemoveClass(element, name) {
    var i, arr1, arr2;
    arr1 = element.className.split(" ");
    arr2 = name.split(" ");
    for (i = 0; i < arr2.length; i++) {
        while (arr1.indexOf(arr2[i]) > -1) {
        arr1.splice(arr1.indexOf(arr2[i]), 1);
        }
    }
    element.className = arr1.join(" ");
    }

    // Add active class to the current control button (highlight it)
    var btnContainer = document.getElementById("myBtnContainer");
    var btns = btnContainer.getElementsByClassName("btn");
    for (var i = 0; i < btns.length; i++) {
    btns[i].addEventListener("click", function() {
        var current = document.getElementsByClassName("active");
        current[0].className = current[0].className.replace(" active", "");
        this.className += " active";
    });
    }
    </script>
    <?php
    if(isset($_GET['cmeta'])){
    
        $commentmeta=$_GET['cmeta'];
        if($commentmeta=='ALL'){
            $wantto= get_comment_meta( $comment_id, 'wantto', true );
            $args = array(
            'post_id' => get_the_ID(),
            
        );
        }else{
            $wantto= get_comment_meta( $comment_id, 'wantto', true );
            $args = array(
            'post_id' => get_the_ID(),
            'meta_query' => array(
            
                array(
                    'key' => 'wantto',
                    'value' => $commentmeta,
                ),
                
            )
        );
        }
        $comment_query = new WP_Comment_Query( $args );
        $comments = $comment_query->comments;
            echo '<ol class="post-comments">';
                wp_list_comments(array(
                    'style'       => 'ol',
                    'short_ping'  => true,
                ));
                echo $wantto;
                
        echo '</ol>';
        /* much simpler one to style probably
                        if ( $comments ) {
                    foreach ( $comments as $comment ) {
                        echo '<p>' . $comment->comment_content . '</p>';
                    }
                } else {
                    echo 'No comments found.';
                }
        */
    }else{

        $wantto= get_comment_meta( $comment_id, 'wantto', true );
        echo '<ol class="post-comments">';
                wp_list_comments(array(
                    'style'       => 'ol',
                    'short_ping'  => true,
                ));
                echo $wantto;
                
        echo '</ol>';
    }
endif;
    $comment_id=get_comment_ID();
    echo $wantto;
    
?>

Here's the section from my functions.php that adds the dropdown menu, and filters by type. I've added the filterDiv tag in the second function of this part. Where I have it now only filters the text, I'd like it to filter the entire comment itself.


/*
 * This will add the fields to the comment form
*/
function wpse406058_custom_comment_fields() {

    echo '<p class="comment-form-wantto">';
    echo '<label for="wantto">Tag your comment so other users can find your post.<br>I want to</label>';
    echo '<select id="wantto" name="wantto" class="myclass">';
        echo '<option value="---">---</option>';
        echo '<option value="BUY">BUY</option>';
        echo '<option value="SELL">SELL</option>';
        echo '<option value="TRADE">TRADE</option>';
        echo '<option value="TALK">TALK</option>';
    echo '</select>';
  
      
}
add_action( 'comment_form_logged_in_after', 'wpse406058_custom_comment_fields' );
add_action( 'comment_form_after_fields', 'wpse406058_custom_comment_fields' );

/*
 * This will field value as comment meta
*/
function wpse406058_save_custom_field($comment_id) {

    if ( isset($_POST['wantto']) && !empty($_POST['wantto']) ) {
        $wantto = sanitize_text_field($_POST['wantto']);
        update_comment_meta( $comment_id, 'wantto', $wantto );
        
    }
    
}
add_action( 'comment_post', 'wpse406058_save_custom_field' );


function wpse406058_display_comment_meta( $comment_text ) {
    
    $wantto = get_comment_meta( get_comment_ID(), 'wantto', true );
  
    if ( isset($wantto) && !empty($wantto) ) {
        
        $wanttotext = '<p class="wantosec">I want to ' . esc_html($wantto) . '</p>';
        ?><div class="filterDiv <?php echo $wantto;?>"><?php
        $comment_text = $wanttotext . $comment_text;
    }
   echo '<div class="container">';
    return $comment_text;
    echo '</div>';
    echo '</div>';
}
add_filter( 'comment_text', 'wpse406058_display_comment_meta' );

When Page First Loads

'SHOW ALL' Selected

'BUY' Selected

3
  • If I'm understanding you correctly in that you'd like to load all comments at once then use the buttons to show/hide varieties of comments, but currently your buttons show/hide only the content of comments - then the solution is not really WordPress-specific. It's just to target the entire comment <div> instead of the content, either by modifying the selector or using JS DOM traversal. If you'd like to modify the markup generated for comments, you could pass the callback argument to wp_list_comments()
    – bosco
    Jun 3 at 17:20
  • Bosco, great to see you again! Was hoping you'd drop by. Yes, you've got it exactly right. I want all the comments to show, then filter out the ones that ARENT the selection. Currently it only filters the content. I don't understand what a JS DOM traversal is. Let me read your links and see if I get that. I think I need to write a function like the others above, that adds the tag to the whole comment, is that right? Let me read before I get ahead of myself. Thanks again bosco!
    – RCOTWUnite
    Jun 3 at 17:25
  • I'm reading the link now. Yes, this looks right. Let me rub my chin and read more. Thanks again
    – RCOTWUnite
    Jun 3 at 17:29

1 Answer 1

0

Currently your comment markup looks similar to the following:

<li class="comment byuser comment-author-rcotwunite odd alt thread-odd thread-alt depth-1" id="comment-7902">
  <div id="div-comment-7902" class="comment-body">
    <div class="comment-author vcard"><!-- ... --></div>
    <div class="comment-meta commentmetadata"><!-- ... --></div>
    <div class="filterDiv SELL show">
      <div class="container">
        <p class="wantosec">I want to SELL</p>
        <p>SUP YALL im here to sell some stuff</p>
        <div class="reply"><!-- ... --></div>
      </div>
    </div>
  </div>
</li>

And the JS which is targeting elements to show/hide:

function filterSelection(c) {
  var x, i;
  x = document.getElementsByClassName("filterDiv");

  if (c == "all") c = "";
  
  // Add the "show" class (display:block) to the filtered elements, and remove the "show" class from the elements that are not selected
  for (i = 0; i < x.length; i++) {
    w3RemoveClass(x[i], "show");
    if (x[i].className.indexOf(c) > -1) w3AddClass(x[i], "show");
  }
}

So the issue is that the class you're using to target elements to show/hide is only applied to a descendant child of the element which represents the full comment (the <li> encapsulating the whole entry, here).


JS DOM Traversal

One solution to targeting that encapsulating <li> instead would be to keep the markup as it is and use the properties on DOM elements to traverse up to the ancestral <li> (though this isn't terribly efficient):

function filterSelection(c) {
  var x, i;
  x = document.getElementsByClassName("filterDiv");

  if (c == "all") c = "";
  
  // Add the "show" class (display:block) to the filtered elements, and remove the "show" class from the elements that are not selected
  for (i = 0; i < x.length; i++) {
    w3RemoveClass(x[i].parentElement.parentElement, "show");
    if (x[i].parentElement.parentElement.className.indexOf(c) > -1) 
      w3AddClass(x[i].parentElement.parentElement, "show");
  }
}

Customize Comment Markup

But perhaps a better solution is to use wp_list_comments()'s callback argument to specify a function to customize each comment's generated markup and apply your HTML classes derived from comment meta-data to the entire comment container instead of just the body.

The best way to start might be to simply copy the function body over from the Walker_Comment::comment() method, which is otherwise responsible for rendering comment markup. And then we'll tweak it to add a couple classes to the containing element if your comment meta exists (I've added PHP comments to designate my modifications):

// functions.php

function wpse406332_render_comment( $comment, $args, $depth ) {
  if ( 'div' === $args['style'] ) {
        $tag       = 'div';
        $add_below = 'comment';
    } else {
        $tag       = 'li';
        $add_below = 'div-comment';
    }
 
    $commenter          = wp_get_current_commenter();
    $show_pending_links = isset( $commenter['comment_author'] ) && $commenter['comment_author'];

    // Set up an array to contain classes to be applied to the `<li>`.
    $classes = [];

    if( $this->has_children )
      array_push( $classes, 'parent' );

    // Check for trade meta
    $wantto = get_comment_meta( get_comment_ID(), 'wantto', true );

    // If trade meta is set, add "filterDiv" and trade type to class list.
    if ( isset($wantto) && !empty($wantto) )
      array_push( $classes, 'filterDiv', $wantto );
 
    if ( $commenter['comment_author_email'] ) {
        $moderation_note = __( 'Your comment is awaiting moderation.' );
    } else {
        $moderation_note = __( 'Your comment is awaiting moderation. This is a preview; your comment will be visible after it has been approved.' );
    }
    ?>

    // Finally, add our modified class list to the comment container.
    <<?php echo $tag; ?> <?php comment_class( $classes, $comment ); ?> id="comment-<?php comment_ID(); ?>">
    <?php if ( 'div' !== $args['style'] ) : ?>
    <div id="div-comment-<?php comment_ID(); ?>" class="comment-body">
    <?php endif; ?>
    <div class="comment-author vcard">
        <?php
        if ( 0 != $args['avatar_size'] ) {
            echo get_avatar( $comment, $args['avatar_size'] );
        }
        ?>
        <?php
        $comment_author = get_comment_author_link( $comment );
 
        if ( '0' == $comment->comment_approved && ! $show_pending_links ) {
            $comment_author = get_comment_author( $comment );
        }
 
        printf(
            /* translators: %s: Comment author link. */
            __( '%s <span class="says">says:</span>' ),
            sprintf( '<cite class="fn">%s</cite>', $comment_author )
        );
        ?>
    </div>
    <?php if ( '0' == $comment->comment_approved ) : ?>
    <em class="comment-awaiting-moderation"><?php echo $moderation_note; ?></em>
    <br />
    <?php endif; ?>
 
    <div class="comment-meta commentmetadata">
        <?php
        printf(
            '<a href="%s">%s</a>',
            esc_url( get_comment_link( $comment, $args ) ),
            sprintf(
                /* translators: 1: Comment date, 2: Comment time. */
                __( '%1$s at %2$s' ),
                get_comment_date( '', $comment ),
                get_comment_time()
            )
        );
 
        edit_comment_link( __( '(Edit)' ), ' &nbsp;&nbsp;', '' );
        ?>
    </div>
 
    <?php
    comment_text(
        $comment,
        array_merge(
            $args,
            array(
                'add_below' => $add_below,
                'depth'     => $depth,
                'max_depth' => $args['max_depth'],
            )
        )
    );
    ?>
 
    <?php
    comment_reply_link(
        array_merge(
            $args,
            array(
                'add_below' => $add_below,
                'depth'     => $depth,
                'max_depth' => $args['max_depth'],
                'before'    => '<div class="reply">',
                'after'     => '</div>',
            )
        )
    );
    ?>
 
    <?php if ( 'div' !== $args['style'] ) : ?>
    </div>
    <?php endif; ?>
    <?php
}

Now modify your wp_comment_list() calls to use the modified render function instead of Walker_Comment's (you'll also want to remove the code which adds those classes to the comment text container from your wpse406058_display_comment_meta() function):

// template file

// ...
wp_list_comments(array(
  'style'       => 'ol',
  'short_ping'  => true,
  'callback'    => 'wpse406332_render_comment',
));
// ...

Wrap-Up

This is by no means an exhaustive list of possibilities. Another solution which wouldn't include modifying the comment markup would be to build a mapping of trade types to <li> elements on page load (using that DOM traversal technique from above), then just referring to those arrays when you need to show/hide comments.

All in all I think customizing the comment markup is the most robust solution, and one which can enable you to further tweak the display for your unique comments use-case (though if your modifications eventually lead to a massive departure from the intent of a simple comment, you might at some point consider using a dedicated post-type for the purpose).

7
  • WOW! Thanks bosco. This is super thorough. I hadn't even considered making a post-type. Out of curiousity, could I replace the whole 'comments' section with a form that makes a new post under the user's name? That actually would help me solve another issue- how to have messages sent about the post show the post when sent. I suppose this is a new question- but is it possible to make a form and have it generate into a new post of post type "WANT TO BUY"?
    – RCOTWUnite
    Jun 3 at 19:05
  • In the meantime, I'm going to spend some time trying this out. Thanks so much!
    – RCOTWUnite
    Jun 3 at 19:08
  • I added the code into my functions and into my comments.php . the filter is not working now. Any ideas? It looks like the filterDiv isn't added?
    – RCOTWUnite
    Jun 3 at 21:40
  • looks like there's an error in the callback function. let me look into that.
    – RCOTWUnite
    Jun 3 at 21:47
  • looks like this part ?> // Finally, add our modified class list to the comment container. <<?php echo $tag; ?> <?php comment_class( $classes, $comment ); ?> id="comment-<?php comment_ID(); ?>"> isn't working right. I changed it to // Finally, add our modified class list to the comment container. ?> <div <?php comment_class( $classes, $comment ); ?> id="comment-<?php comment_ID(); ?>"> and the error went away, but the javascript filter doesn't work
    – RCOTWUnite
    Jun 3 at 21:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.