Helpful? Yes/No Feedback Widget
Appends a "Was this helpful?" thumbs-up/down widget after single post content; votes are stored in post meta and the user's choice is remembered via a cookie.
Module3 parts · by SnipCraft
PHPLogic
php
<?php
add_filter( 'the_content', 'scseed_helpful_append', 25 );
function scseed_helpful_append( $content ) {
if ( ! is_single() || ! in_the_loop() || ! is_main_query() ) { return $content; }
$pid = get_the_ID();
$yes = (int) get_post_meta( $pid, '_scseed_helpful_yes', true );
$no = (int) get_post_meta( $pid, '_scseed_helpful_no', true );
$nonce = wp_create_nonce( 'scseed_helpful_' . $pid );
$widget = '<div class="scseed-helpful" data-pid="' . absint( $pid ) . '" data-nonce="' . esc_attr( $nonce ) . '">'
. '<p class="scseed-helpful-q">' . esc_html__( 'Was this article helpful?', 'scseed' ) . '</p>'
. '<div class="scseed-helpful-btns">'
. '<button class="scseed-helpful-btn scseed-helpful-yes" type="button" data-vote="yes" aria-label="Yes">'
. '<span class="scseed-helpful-icon" aria-hidden="true">👍</span>'
. '<span class="scseed-helpful-label">' . esc_html__( 'Yes', 'scseed' ) . '</span>'
. '<span class="scseed-helpful-count">' . absint( $yes ) . '</span>'
. '</button>'
. '<button class="scseed-helpful-btn scseed-helpful-no" type="button" data-vote="no" aria-label="No">'
. '<span class="scseed-helpful-icon" aria-hidden="true">👎</span>'
. '<span class="scseed-helpful-label">' . esc_html__( 'No', 'scseed' ) . '</span>'
. '<span class="scseed-helpful-count">' . absint( $no ) . '</span>'
. '</button>'
. '</div>'
. '<p class="scseed-helpful-thanks" aria-live="polite"></p>'
. '</div>';
return $content . $widget;
}
add_action( 'wp_ajax_scseed_helpful', 'scseed_helpful_ajax' );
add_action( 'wp_ajax_nopriv_scseed_helpful', 'scseed_helpful_ajax' );
function scseed_helpful_ajax() {
$pid = absint( isset( $_POST['pid'] ) ? $_POST['pid'] : 0 );
$vote = sanitize_key( isset( $_POST['vote'] ) ? $_POST['vote'] : '' );
$nonce = sanitize_text_field( isset( $_POST['nonce'] ) ? wp_unslash( $_POST['nonce'] ) : '' );
if ( ! $pid || ! in_array( $vote, array( 'yes', 'no' ), true ) ) {
wp_send_json_error( 'invalid' );
}
if ( ! wp_verify_nonce( $nonce, 'scseed_helpful_' . $pid ) ) {
wp_send_json_error( 'nonce' );
}
$key = '_scseed_helpful_' . $vote;
$cur = (int) get_post_meta( $pid, $key, true );
update_post_meta( $pid, $key, $cur + 1 );
wp_send_json_success( array(
'yes' => (int) get_post_meta( $pid, '_scseed_helpful_yes', true ),
'no' => (int) get_post_meta( $pid, '_scseed_helpful_no', true ),
) );
}CSSStyles
css
.scseed-helpful {
margin: 2rem 0 1rem;
padding: 1.25rem 1.5rem;
background: #fafbfc;
border: 1px solid #e8eaed;
border-radius: 8px;
text-align: center;
}
.scseed-helpful-q {
font-size: 0.95rem;
font-weight: 600;
color: #333;
margin: 0 0 0.75rem;
}
.scseed-helpful-btns {
display: flex;
justify-content: center;
gap: 0.75rem;
}
.scseed-helpful-btn {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.5rem 1.25rem;
background: #fff;
border: 1px solid #d0d0d0;
border-radius: 99px;
font-size: 0.875rem;
cursor: pointer;
transition: background 0.2s, border-color 0.2s, transform 0.15s;
font-family: inherit;
color: #444;
}
.scseed-helpful-btn:hover { background: #f5f5f5; border-color: #aaa; }
.scseed-helpful-btn.scseed-voted { font-weight: 700; transform: scale(1.05); }
.scseed-helpful-yes.scseed-voted { background: #f0fff4; border-color: #4caf50; color: #2e7d32; }
.scseed-helpful-no.scseed-voted { background: #fff8f0; border-color: #ff9800; color: #e65100; }
.scseed-helpful-btn[disabled] { opacity: 0.6; cursor: default; }
.scseed-helpful-icon { font-size: 1rem; line-height: 1; }
.scseed-helpful-count { font-weight: 700; min-width: 1ch; }
.scseed-helpful-thanks { font-size: 0.85rem; color: #4caf50; margin: 0.5rem 0 0; min-height: 1.2em; }JavaScriptScript
js
(function() {
'use strict';
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.scseed-helpful').forEach(function(widget) {
var pid = widget.dataset.pid;
var cookie = 'scseed_helpful_' + pid;
// Restore voted state
var voted = getCookie(cookie);
if ( voted ) { lockWidget(widget, voted, null); }
widget.querySelectorAll('.scseed-helpful-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
if ( btn.disabled ) { return; }
var vote = btn.dataset.vote;
var nonce = widget.dataset.nonce;
var ajax = (typeof ajaxurl !== 'undefined') ? ajaxurl : '/wp-admin/admin-ajax.php';
btn.disabled = true;
var fd = new FormData();
fd.append('action', 'scseed_helpful');
fd.append('pid', pid);
fd.append('vote', vote);
fd.append('nonce', nonce);
fetch(ajax, { method: 'POST', body: fd })
.then(function(r) { return r.json(); })
.then(function(data) {
if ( data.success ) {
lockWidget(widget, vote, data.data);
setCookie(cookie, vote, 365);
}
})
.catch(function() { btn.disabled = false; });
});
});
});
function lockWidget(widget, vote, counts) {
widget.querySelectorAll('.scseed-helpful-btn').forEach(function(b) {
b.disabled = true;
if ( b.dataset.vote === vote ) { b.classList.add('scseed-voted'); }
});
if ( counts ) {
widget.querySelector('.scseed-helpful-yes .scseed-helpful-count').textContent = counts.yes;
widget.querySelector('.scseed-helpful-no .scseed-helpful-count').textContent = counts.no;
}
var thanks = widget.querySelector('.scseed-helpful-thanks');
if ( thanks ) { thanks.textContent = vote === 'yes' ? 'Thanks for your feedback!' : 'Thanks — we'll work to improve it.'; }
}
function setCookie(name, val, days) {
var d = new Date();
d.setTime(d.getTime() + days * 86400000);
document.cookie = name + '=' + val + ';expires=' + d.toUTCString() + ';path=/;SameSite=Lax';
}
function getCookie(name) {
var parts = ('; ' + document.cookie).split('; ' + name + '=');
return parts.length === 2 ? parts.pop().split(';').shift() : '';
}
});
}());#ux#posts#engagement#rating#feedback