Simple AJAX comments form handling without a plugin and jQuery as a dependency
- a rewrite in pure JS of an example writen in jQuery found here: https://rudrastyh.com/wordpress/ajax-comments.html
Simple AJAX comments form handling without a plugin and jQuery as a dependency
| // AXIOS is a dependency for dealing with AJAX stuff (npm i axios) | |
| import axios from 'axios'; | |
| /* | |
| * Let's begin with validation functions | |
| */ | |
| import axios from 'axios'; | |
| // Helper serialize function | |
| function serialize(form) { | |
| let field, s = []; | |
| if (typeof form == 'object' && form.nodeName == "FORM") { | |
| const len = form.elements.length; | |
| for (let i=0; i<len; i++) { | |
| field = form.elements[i]; | |
| if ( | |
| field.name && | |
| !field.disabled && | |
| field.type != 'file' && | |
| field.type != 'checkbox' && | |
| field.type != 'reset' && | |
| field.type != 'submit' && | |
| field.type != 'button' | |
| ) { | |
| s[s.length] = encodeURIComponent(field.name) + "=" + encodeURIComponent(field.value); | |
| } // endIf | |
| } // endFor | |
| } // endIf | |
| return s.join('&').replace(/%20/g, '+'); | |
| } // end serialize fn | |
| // VALIDATION FUNCTIONS | |
| // general validate | |
| function validate($this) { | |
| if ($this.value.length < 3) { | |
| $this.classList.add('error'); | |
| return false; | |
| } else { | |
| $this.classList.remove('error'); | |
| return true; | |
| } | |
| }; | |
| // email validate | |
| function validateEmail($this) { | |
| const emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/, | |
| emailToValidate = $this.value; | |
| if (!emailReg.test(emailToValidate) || emailToValidate == "") { | |
| $this.classList.add('error'); | |
| return false; | |
| } else { | |
| $this.classList.remove('error'); | |
| return true; | |
| } | |
| }; | |
| function tryAndSubmitComment() { | |
| const $commentForm = document.getElementById('commentform'); | |
| // run this on document loaded if on single blog post page | |
| $commentForm.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| const $commentsWrap = document.querySelector('.comments-wrap'), | |
| $button = $commentsWrap.querySelector('#submit'), | |
| $respond = $commentsWrap.querySelector('#respond'), | |
| $commentlist = $commentsWrap.querySelector('.comment-list'), | |
| $cancelreplylink = $commentsWrap.querySelector('#cancel-comment-reply-link'), | |
| $author = $commentsWrap.querySelector('#author'), | |
| $email = $commentsWrap.querySelector('#email'), | |
| $comment = $commentsWrap.querySelector('#comment'); | |
| // if user is logged in, do not validate author and email fields | |
| if($author) { validate($author); console.log('author'); } | |
| if($email) { validateEmail($email); console.log('email');} | |
| // validate comment in any case | |
| validate($comment); | |
| // if comment form isn't in process, submit it | |
| if ( | |
| !$button.classList.contains('loadingform') | |
| && !$author.classList.contains('error') | |
| && !$email.classList.contains('error') | |
| && !$comment.classList.contains('error') | |
| ) { | |
| const serializedData = serialize($commentForm) + '&action=ajaxcomments'; | |
| // add loading text to button ad disable it during request | |
| $button.classList.add('loadingform'); | |
| $button.value = 'Sending...'; | |
| $button.disabled = 'disabled'; | |
| // Start ajax req | |
| axios.post(gbs.ajax, serializedData) | |
| // destructure data and save as $addedCommentHTML variable | |
| .then(({ data : $addedCommentHTML }) => { | |
| // if this post already has comments | |
| if($commentlist) { | |
| // if in reply to another comment | |
| if($respond.parentNode.classList.contains('comment')) { | |
| // if the other replies exist | |
| if($respond.parentNode.querySelector('.children')){ | |
| $respond.parentNode.querySelector('.children') | |
| .lastElementChild.insertAdjacentHTML('afterend', $addedCommentHTML); | |
| } else { | |
| // if no replies, add <ol class="children"> | |
| $addedCommentHTML = `<ol class="children">${$addedCommentHTML}</ol>`; | |
| $respond.parentNode.querySelector('article').insertAdjacentHTML('afterend', $addedCommentHTML); | |
| } | |
| // close respond form | |
| $cancelreplylink.click(); | |
| } else { | |
| // simple comment, if there is at least one comment present | |
| $commentlist.lastElementChild.insertAdjacentHTML('afterend', $addedCommentHTML); | |
| } | |
| } else{ | |
| // if no comments yet | |
| $addedCommentHTML = '<ol class="comment-list">' + $addedCommentHTML + '</ol>'; | |
| $respond.insertAdjacentHTML('beforebegin', $addedCommentHTML); | |
| } | |
| // clear textarea field | |
| $comment.value = ''; | |
| }) | |
| .catch( error => { | |
| console.log(error); | |
| alert("We couldn't post your comment at this moment. Please try again later."); | |
| }) | |
| .finally(() => { | |
| $button.classList.remove('loadingform'); | |
| $button.value = 'Post Comment...'; | |
| $button.removeAttribute('disabled'); | |
| }); | |
| } // if comment form isn't in process, submit it | |
| return false; | |
| }); // onSubmit | |
| } // tryAndSubmitComment | |
| // DOC IS READY | |
| document.addEventListener("DOMContentLoaded", () => { | |
| if (document.querySelector('.comments-wrap')) { | |
| // call AJAX comments function here | |
| tryAndSubmitComment(); | |
| } | |
| }); // end doc ready |
| <?php | |
| add_action( 'wp_ajax_ajaxcomments', 'ugwps_submit_ajax_comment' ); // wp_ajax_{action} for registered user | |
| add_action( 'wp_ajax_nopriv_ajaxcomments', 'ugwps_submit_ajax_comment' ); // wp_ajax_nopriv_{action} for not registered users | |
| function ugwps_submit_ajax_comment(){ | |
| $comment = wp_handle_comment_submission( wp_unslash( $_POST ) ); | |
| if ( is_wp_error( $comment ) ) { | |
| $error_data = intval( $comment->get_error_data() ); | |
| if ( ! empty( $error_data ) ) { | |
| wp_die( '<p>' . $comment->get_error_message() . '</p>', __( 'Comment Submission Failure' ), array( 'response' => $error_data, 'back_link' => true ) ); | |
| } else { | |
| wp_die( 'Unknown error' ); | |
| } | |
| } | |
| /* | |
| * Set Cookies | |
| */ | |
| $user = wp_get_current_user(); | |
| do_action('set_comment_cookies', $comment, $user); | |
| /* | |
| * If you do not like this loop, pass the comment depth from JavaScript code | |
| */ | |
| $comment_depth = 1; | |
| $comment_parent = $comment->comment_parent; | |
| while( $comment_parent ){ | |
| $comment_depth++; | |
| $parent_comment = get_comment( $comment_parent ); | |
| $comment_parent = $parent_comment->comment_parent; | |
| } | |
| /* | |
| * Set the globals, so our comment functions below will work correctly | |
| */ | |
| $GLOBALS['comment'] = $comment; | |
| $GLOBALS['comment_depth'] = $comment_depth; | |
| /* | |
| * Here is the comment template, you can configure it for your website | |
| * or you can try to find a ready function in your theme files | |
| */ | |
| $comment_html = '<li ' . comment_class('', null, null, false ) . ' id="comment-' . get_comment_ID() . '"> | |
| <article class="comment-body" id="div-comment-' . get_comment_ID() . '"> | |
| <footer class="comment-meta"> | |
| <div class="comment-author vcard"> | |
| ' . get_avatar( $comment, 56 ) . ' | |
| <b class="fn">' . get_comment_author_link() . '</b> <span class="says">says:</span> | |
| </div> | |
| <div class="comment-metadata"> | |
| <a href="' . esc_url( get_comment_link( $comment->comment_ID ) ) . '"><time datetime="'. get_comment_date('c') .'">' . sprintf('%1$s at %2$s', get_comment_date(), get_comment_time() ) . '</time></a>'; | |
| if( $edit_link = get_edit_comment_link() ) | |
| $comment_html .= '<span class="edit-link"><a class="comment-edit-link" href="' . $edit_link . '">Edit</a></span>'; | |
| $comment_html .= '</div>'; | |
| if ( $comment->comment_approved == '0' ) | |
| $comment_html .= '<p class="comment-awaiting-moderation">Your comment is awaiting moderation.</p>'; | |
| $comment_html .= '</footer> | |
| <div class="comment-content">' . apply_filters( 'comment_text', get_comment_text( $comment ), $comment ) . '</div> | |
| </article> | |
| </li>'; | |
| echo $comment_html; | |
| die(); | |
| } |
| <?php | |
| /** | |
| * The template for displaying comments | |
| * | |
| * This is the template that displays the area of the page that contains both the current comments | |
| * and the comment form. | |
| */ | |
| /* | |
| * If the current post is protected by a password and | |
| * the visitor has not yet entered the password we will | |
| * return early without loading the comments. | |
| */ | |
| ?> | |
| <?php if ( post_password_required() ) { return; } ?> | |
| <div id="comments" class="comments-area"> | |
| <?php | |
| // You can start editing here -- including this comment! | |
| if ( have_comments() ) : | |
| ?> | |
| <h2 class="comments-title"> | |
| <?php | |
| $wrwps_comment_count = get_comments_number(); | |
| if ( '1' === $wrwps_comment_count ) { | |
| printf('<span>1 Comment</span>'); | |
| } else { | |
| printf('<span>' . $wrwps_comment_count . ' Comments</span>'); | |
| } | |
| ?> | |
| </h2><!-- .comments-title --> | |
| <?php the_comments_navigation(); ?> | |
| <ol class="comment-list"> | |
| <?php | |
| wp_list_comments( array( | |
| 'style' => 'ol', | |
| 'short_ping' => true, | |
| 'avatar_size' => 56, | |
| ) ); | |
| ?> | |
| </ol><!-- .comment-list --> | |
| <?php | |
| the_comments_navigation(); | |
| // If comments are closed and there are comments, let's leave a little note, shall we? | |
| if ( ! comments_open() ) : | |
| ?> | |
| <p class="no-comments"><?php esc_html_e( 'Comments are closed.', 'wrwps' ); ?></p> | |
| <?php | |
| endif; | |
| endif; // Check for have_comments(). | |
| comment_form(); | |
| ?> | |
| </div><!-- #comments --> |
| // You can replace variables that are used here with your color palette | |
| .comments-area { | |
| background-color: $c-gray; | |
| border: 1px solid $c-silver; | |
| border-radius: 4px; | |
| margin-top: 48px; | |
| padding: 24px 24px 32px; | |
| .comment { | |
| padding: 40px 0 50px; | |
| border-bottom: 1px solid $c-silver; | |
| .children { | |
| list-style: none; | |
| li:last-of-type { | |
| border-bottom: 0; | |
| padding-bottom: 0; | |
| } | |
| } | |
| &-author { | |
| .fn { | |
| font-size: 20px; | |
| font-weight: 600; | |
| line-height: normal; | |
| color: #434f58; | |
| text-transform: capitalize; | |
| } | |
| } | |
| &-metadata { | |
| time { | |
| font-size: 14px; | |
| font-weight: 600; | |
| line-height: 1.71; | |
| color: #222; | |
| } | |
| .edit-link { | |
| display: none; | |
| } | |
| } | |
| &-notes { | |
| display: none; | |
| } | |
| &-reply-title { | |
| margin-bottom: 24px; | |
| } | |
| &-form { | |
| label { | |
| display: block; | |
| font-size: 14px; | |
| font-weight: bold; | |
| line-height: normal; | |
| .required { | |
| display: none; | |
| } | |
| } | |
| textarea { | |
| width: 100%; | |
| min-height: 105px; | |
| resize: vertical; | |
| } | |
| textarea, | |
| input { | |
| border: 1px solid $c-silver; | |
| border-radius: 4px; | |
| padding: 16px; | |
| &:focus { | |
| outline-color: $c-brand; | |
| } | |
| } | |
| input { | |
| width: 100%; | |
| height: 48px; | |
| font-size: 16px; | |
| font-weight: normal; | |
| font-style: normal; | |
| font-stretch: normal; | |
| line-height: normal; | |
| letter-spacing: normal; | |
| color: #434f58; | |
| } | |
| &-comment { | |
| margin-bottom: 20px; | |
| } | |
| &-url { | |
| display: none; | |
| } | |
| &-author { | |
| margin-right: 20px; | |
| } | |
| &-author, | |
| &-email { | |
| display: inline-block; | |
| width: calc((100% - 25px) / 2) | |
| } | |
| .submit { | |
| @extend .btn; | |
| @extend .btn--sec; | |
| width: 240px; | |
| margin-top: 20px; | |
| font-size: 16px; | |
| cursor: pointer; | |
| &.loadingform { | |
| background: orange; | |
| } | |
| &[disabled] { | |
| background: #555; | |
| cursor: not-allowed; | |
| } | |
| } | |
| } | |
| } | |
| .comment-reply-link { | |
| font-size: 14px; | |
| font-weight: 600; | |
| line-height: 1.43; | |
| color: #0073ec; | |
| } | |
| } | |
| .comments-title { | |
| border-bottom: 1px solid $c-silver; | |
| margin-bottom: 0; | |
| padding-bottom: 22px; | |
| } | |
| .comment-list { | |
| list-style: none; | |
| padding-left: 0; | |
| } | |
| .says { | |
| display: none; | |
| } | |
| .avatar { | |
| border-radius: 3px; | |
| margin-right: 24px; | |
| } | |
| .comment-metadata { | |
| margin-top: -22px; | |
| margin-left: 84px; | |
| } | |
| .comment-content { | |
| margin-left: 84px; | |
| margin-top: 15px; | |
| } | |
| .reply { | |
| margin-left: 84px; | |
| } | |
| .comment-form-wrapper-flex { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .form-submit { | |
| display: flex; | |
| justify-content: flex-end; | |
| } |
| <?php | |
| /** | |
| * Enqueue scripts and styles. | |
| */ | |
| function wwrwps_scripts() { | |
| // STYLES | |
| // Main css file | |
| wp_enqueue_style( 'wrwps-gbs-style', get_stylesheet_uri() ); | |
| // SCRIPTS | |
| // Main js file | |
| wp_enqueue_script( 'wrwps-gbs-main', get_template_directory_uri() . '/app.js', array(), false, true ); | |
| // for custom AJAX comment form handling | |
| wp_localize_script('wrwps-gbs-main', 'gbs', ['ajax' => admin_url('admin-ajax.php')]); | |
| if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) { | |
| wp_enqueue_script( 'comment-reply' ); | |
| } | |
| } | |
| add_action( 'wp_enqueue_scripts', 'wrwps_scripts' ); |
| // Include Comment form ajax processing | |
| require get_template_directory() . '/inc/CommentFormProcessing.php'; | |
| // Include enqueues file | |
| require get_template_directory() . '/inc/enqueues.php'; | |
| /** | |
| * ADD CUSTOM COMMENT FORM WRAP FOR FLEX STYLES | |
| */ | |
| function ugwps_comment_form_before_fields() { | |
| echo '<div class="comment-form-wrapper-flex">'; | |
| } | |
| add_action('comment_form_before_fields', 'ugwps_comment_form_before_fields'); | |
| function ugwps_comment_form_after_fields() { | |
| echo '</div>'; | |
| } | |
| add_action('comment_form_after_fields', 'ugwps_comment_form_after_fields'); |
| // Add this snippet where you want to show you comments (below the content) | |
| <div class="comments-wrap"> | |
| <?php | |
| // If comments are open or we have at least one comment, load up the comment template. | |
| if ( comments_open() || get_comments_number() ) : | |
| comments_template(); | |
| endif; ?> | |
| </div> |