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> |