Skip to content

Instantly share code, notes, and snippets.

@nikola-wd
Last active March 12, 2023 20:11
Show Gist options
  • Save nikola-wd/35df275fae26c9cbb50707a877575b04 to your computer and use it in GitHub Desktop.
Save nikola-wd/35df275fae26c9cbb50707a877575b04 to your computer and use it in GitHub Desktop.
Wordpress AJAX comment form in vanilla JS (es6) + axios (Comment submission without page refresh)
// 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment