Help with AJAX front end comment moderation

Seems to me that the only problem is that you put the full approve url in the link, in this ways when you click the link you trigger the ajax and open the page url in the link.

To be more clear your link is something like:

<a class="p3-comment-moderation" href="https://wordpress.stackexchange.com/questions/128383/admin-ajax.php?action=p3_comment_approve&comment_id=123&nonce=xxxxx" data-comment_id="123" data-nonce="xxxxx">Approve</a>

thanks to the javascript, when you click on the link you launch an ajax request but, there is nothing that prevent the default link behaviour to be triggered, and so the admin-ajax.php is required to the browser, and thanks to the header statement in your code, then the page comes back again to current page.

So, actually, this is what happen:

  1. the ajax is triggered
  2. admin-ajax.php is open by the url in the a tag
  3. the page is sended again to previous page because of the header("Location: ".$_SERVER["HTTP_REFERER"]) statement in your code

I know that putting the full url in the link is the right way to make it works even if the user has javascript disabled, but when javascript is enabled you must prevent the default link behaviour, this can be done via the preventDefault() js function.

So, in your javascript replace

jQuery(".p3-comment-moderation").click( function() {

with

jQuery(".p3-comment-moderation").click( function(e) {
    e.preventDefault();

After that everything should work as expected.

If, like I guess, this is the only problem, this question is a pure javascript one, so off-topic here. However, to make my answer more WordPress relevant I want add some tips (nothing that make the script work or not, just best practice and improvements).

Tip 1: Escape attributes and links

When in WordPress you output something in the html output as tag property, best practice is escape it with esc_attr, in general use all esc_* function when applyable

$p3_edit_links="<a class="p3-comment-moderation" href="" . $p3_approve_link . '" data-comment_id="' . $comment_id . '" data-nonce="' . $nonce . '">Approve</a>';

is better wrote:

$p3_edit_links_f="<a class="p3-comment-moderation" href="https://wordpress.stackexchange.com/questions/128383/%s" data-comment_id="%d" data-nonce="https://wordpress.stackexchange.com/questions/128383/%s">";
$p3_edit_links = sprintf($p3_edit_links_f, esc_url($p3_approve_link), esc_attr($comment_id), esc_attr($nonce) );

I know that you getting urls and attributes from WP core functions, however once almost all output from core can be filtered is good to apply data validation (although data validation functions can filtered too…)

Tip 2: use core constant to check ajax requests

In your code there’s

if( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {

to check if the current request is from ajax. In WordPress when you properly use admin-ajax.php (like you do) you can check ajax request looking for DOING_AJAX constant, so the previous conditional statement becomes:

if ( defined('DOING_AJAX') && DOING_AJAX ) {

Tip 3: use core function to output json and exit script

Your code

$result = json_encode($result);
echo $result;
...
die();

can be wrote in one line, using the core wp_send_json function

wp_send_json( $result );

Tip 4: avoid direct superglobals access

when you have to handle superglobals ($_REQUEST, $_GET, $_POST…) to avoid notices you should always check that the key you are looking for is setted, so your

if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'p3_comment_moderation' ) ) {

is better wrote:

if ( ! isset($_REQUEST['nonce']) || ! wp_verify_nonce( $_REQUEST['nonce'], 'p3_comment_moderation' ) ) {

However, is even better to use filter_var instead of accessing superglobals directly

$nonce = filter_var( INPUT_GET, 'nonce', FILTER_VALIDATE_STRING);
if ( empty($nonce) ) $nonce = filter_var( INPUT_POST, 'nonce', FILTER_VALIDATE_STRING);
if ( ! wp_verify_nonce( $nonce, 'p3_comment_moderation' ) ) {