Comment Submission Without Page Refresh. No Plugins Used.

I know there are so much ready services for website comments like facebook and disqus but I like the default WordPress comment system the most because of the following benefits:

AJAX Comments Requirements

Before we begin coding there would be some more text to read. I suggest you to overview the AJAX comment functionality we will create.

Step 1. Can not imagine that HTML could be the most difficult part? Surprise!

If you do not have the advanced level of knowing such things as DOM traversal, please, be very careful at this step.

I begin with the words that each WordPress theme differs. In this post I will make the AJAX comments for WordPress default TwentySeventeen theme, but I can not even imagine what theme you will use.

You shouldn’t copy this code anywhere – just look if your theme comment structure is similar to it, if no – you should make changes either in your theme HTML structure or in Step 3 of this tutorial.

<!-- it can be <ul> or <ol> element -->
<ol class="comment-list">
	<!-- more classes can be inside, we need only "comment" and "depth-#" -->
	<li id="comment-1" class="comment depth-1">
		<!-- article tag can also have ID and Classes but it doesn't matter for us -->
			<!-- comment content and comment meta is here -->
			<!-- just make sure that <div class="reply"> is the last element inside article -->
			<div class="reply">
				<a class="comment-reply-link">Reply</a>
		<!-- nested comment replies are here  -->
		<ol class="children">
			<!-- comment <li> elements are the same like in depth-1 level -->

<div id="respond">
	<!-- form is here -->
	<!-- make sure that all the name attributes and ID of the <form> are correct -->

Step 2. Enqueue Scripts in functions.php

Our task in this step is to include to our website pages jQuery, our custom script and print the URL of the WordPress AJAX.

For your functions.php:

add_action( 'wp_enqueue_scripts', 'misha_ajax_comments_scripts' );

function misha_ajax_comments_scripts() {
	// I think jQuery is already included in your theme, check it yourself
	// just register for now, we will enqueue it below
	wp_register_script( 'ajax_comment', get_stylesheet_directory_uri() . '/ajax-comment.js', array('jquery') );
	// let's pass ajaxurl here, you can do it directly in JavaScript but sometimes it can cause problems, so better is PHP
	wp_localize_script( 'ajax_comment', 'misha_ajax_comment_params', array(
		'ajaxurl' => site_url() . '/wp-admin/admin-ajax.php'
	) );
 	wp_enqueue_script( 'ajax_comment' );

First of all let’s check if everything has been made correctly. Open your website, then go to browser console. There should be a 404 error for ajax-comment.js.

404 error in Google Chrome Console.

No error? Hmmm, haven’t you created ajax-comment.js yet? If not, check that your theme header has wp_head() function and your theme footer has wp_footer() function.

Only when you got this 404 error, you can create ajax-comment.js file in your theme directory. I’m asking about it because if you’re a beginner in coding, this small debug tip will be helpful for you.

Also open your website source HTML and check if ajaxurl parameter is there. Is everything ok? Proceed to the next step.

Step 3. jQuery code for ajax-comment.js

If your HTML comment structure is the same as described below or maybe your are using TwentySeventeen theme – in these cases you can insert the below code without changes at all.

 * Let's begin with validation functions
 jQuery.extend(jQuery.fn, {
	 * check if field value lenth more than 3 symbols ( for name and comment ) 
	validate: function () {
		if (jQuery(this).val().length < 3) {jQuery(this).addClass('error');return false} else {jQuery(this).removeClass('error');return true}
	 * check if email is correct
	 * add to your CSS the styles of .error field, for example border-color:red;
	validateEmail: function () {
		var emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/,
		    emailToValidate = jQuery(this).val();
		if (!emailReg.test( emailToValidate ) || emailToValidate == "") {
			jQuery(this).addClass('error');return false
		} else {
			jQuery(this).removeClass('error');return true

	 * On comment form submit
	$( '#commentform' ).submit(function(){
		// define some vars
		var button = $('#submit'), // submit button
		    respond = $('#respond'), // comment form container
		    commentlist = $('.comment-list'), // comment list container
		    cancelreplylink = $('#cancel-comment-reply-link');
		// if user is logged in, do not validate author and email fields
		if( $( '#author' ).length )
			$( '#author' ).validate();
		if( $( '#email' ).length )
			$( '#email' ).validateEmail();
		// validate comment in any case
		$( '#comment' ).validate();
		// if comment form isn't in process, submit it
		if ( !button.hasClass( 'loadingform' ) && !$( '#author' ).hasClass( 'error' ) && !$( '#email' ).hasClass( 'error' ) && !$( '#comment' ).hasClass( 'error' ) ){
			// ajax request
				type : 'POST',
				url : misha_ajax_comment_params.ajaxurl, // admin-ajax.php URL
				data: $(this).serialize() + '&action=ajaxcomments', // send form data + action parameter
				beforeSend: function(xhr){
					// what to do just after the form has been submitted
				error: function (request, status, error) {
					if( status == 500 ){
						alert( 'Error while adding comment' );
					} else if( status == 'timeout' ){
						alert('Error: Server doesn\'t respond.');
					} else {
						// process WordPress errors
						var wpErrorHtml = request.responseText.split("<p>"),
							wpErrorStr = wpErrorHtml[1].split("</p>");
						alert( wpErrorStr[0] );
				success: function ( addedCommentHTML ) {
					// if this post already has comments
					if( commentlist.length > 0 ){
						// if in reply to another comment
						if( respond.parent().hasClass( 'comment' ) ){
							// if the other replies exist
							if( respond.parent().children( '.children' ).length ){	
								respond.parent().children( '.children' ).append( addedCommentHTML );
							} else {
								// if no replies, add <ol class="children">
								addedCommentHTML = '<ol class="children">' + addedCommentHTML + '</ol>';
								respond.parent().append( addedCommentHTML );
							// close respond form
						} else {
							// simple comment
							commentlist.append( addedCommentHTML );
						// if no comments yet
						addedCommentHTML = '<ol class="comment-list">' + addedCommentHTML + '</ol>';
						respond.before( $(addedCommentHTML) );
					// clear textarea field
				complete: function(){
					// what to do after a comment has been added
					button.removeClass( 'loadingform' ).val( 'Post Comment' );
		return false;

Do you want me to describe how AJAX in WordPress works?

  1. WordPress AJAX URL is always the same /wp-admin/admin-ajax.php.
  2. You pass parameters to the script as usual, but you should add action parameter (it can be a hidden field in your form as well).
  3. The action parameter is connected to hooks in functions.phpwp_ajax_{action} for registered users and wp_ajax_nopriv_{action} for unregistered ones.

Step 4. Generate and Publish a Comment in PHP

In the code below there is a function wp_handle_comment_submission() that appeared in WordPress 4.4. If you need the code for the earlier version of WP, just ask me in comments.

add_action( 'wp_ajax_ajaxcomments', 'misha_submit_ajax_comment' ); // wp_ajax_{action} for registered user
add_action( 'wp_ajax_nopriv_ajaxcomments', 'misha_submit_ajax_comment' ); // wp_ajax_nopriv_{action} for not registered users

function misha_submit_ajax_comment(){
	 * Wow, this cool function appeared in WordPress 4.4.0, before that my code was muuuuch mooore longer
	 * @since 4.4.0
	$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 ){
		$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, 100 ) . '
					<b class="fn">' . get_comment_author_link() . '</b> <span class="says">says:</span>
				<div class="comment-metadata">
					<a href="' . esc_url( get_comment_link( $comment->comment_ID ) ) . '">' . sprintf('%1$s at %2$s', get_comment_date(),  get_comment_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>
	echo $comment_html;


Some ideas for you, I didn’t add it to the post because I want to make it as simple as possible:

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X