This article is an advanced section for Enhanced SEO: Option 2. You can return to the main Option 2 documentation here.
Implementing the on_change Callback (Advanced Option 2)
Level of Effort: Medium-High
The on_change callback is an advanced feature that allows you to add detailed Review schema to your product pages. While the on_render callback provides the data for an AggregateRating, the on_change callback provides the full details of individual reviews as they are displayed on the page.
This is the ideal solution if you want your structured data to update dynamically as users filter, sort, or paginate through reviews.
Important: The on_change callback is a direct replacement for the on_render function. You must use one or the other in your pwr.render() call; you cannot use both on the same page.
How to Implement:
Add the on_change function to your pwr.render() configuration. The function will trigger whenever the list of reviews changes, allowing you to build and attach the Review schema objects to your existing Product schema.
Please note the following critical points:
- Code Requires Modification: The example below is a template and will need to be modified for your specific site structure and needs.
-
Schema Structure Assumption: This code assumes your
Productschema script tag (identified here asproductschema) contains an array of product objects. If your schema is a single product object (which is more common), you must remove theforloops that iterate throughscriptTagObjectand instead attach the reviews directly to the single object (e.g.,scriptTagObject.review = reviews;). -
Element ID: The example uses
getElementById('productschema')to find your product schema. You must ensure your schema script tag has this ID or update the selector to match your site.
Example:
<script>
pwr("render", {
api_key: "YOUR_API_KEY", // Provided by PowerReviews
locale: "en_US", // Provided by PowerReviews
merchant_group_id: "YOUR_MERCHANT_GROUP_ID", // Provided by PowerReviews
merchant_id: "YOUR_MERCHANT_ID", // Provided by PowerReviews
page_id: "YOUR_PAGE_ID",
review_wrapper_url: "YOUR_REVIEW_WRAPPER_URL",
on_change: function(config, data) {
// Only trigger when the ReviewList component updates
if (config.component === 'ReviewList') {
// First, find the existing product schema on the page
var scriptTag = document.getElementById('productschema');
if (!scriptTag) {
console.log("PowerReviews SEO: Product schema tag not found.");
return; // Exit if the schema tag doesn't exist
}
var scriptTagObject = JSON.parse(scriptTag.innerHTML);
// CASE 1: Reviews are present, so build the schema
if (data.review_count > 0 && data.reviews && data.reviews.length > 0) {
var reviews = [];
// Loop through each review provided by the callback
for (var i = 0; i < data.reviews.length; i++) {
var pwrReview = data.reviews[i];
var review = {
"@type": "Review",
"name": pwrReview.details.headline,
"reviewBody": pwrReview.details.comments,
"datePublished": new Date(pwrReview.details.created_date).toISOString(),
"author": {
"@type": "Person",
"name": pwrReview.details.nickname
},
"reviewRating": {
"@type": "Rating",
"ratingValue": pwrReview.metrics.rating
}
};
// Optionally add location if it exists and is not undisclosed
if (pwrReview.details.location && (pwrReview.details.location).toLowerCase() != 'undisclosed') {
review["locationCreated"] = {
"@type": "AdministrativeArea",
"name": pwrReview.details.location
};
}
reviews.push(review);
}
// Attach the array of reviews to the Product schema
// WARNING: This code assumes your schema is an ARRAY of products.
// If it's a single object, change this loop to: scriptTagObject.review = reviews;
for (var j = 0; j < scriptTagObject.length; j++) {
if (scriptTagObject[j]) {
scriptTagObject[j]['review'] = reviews;
}
}
// CASE 2: No reviews are visible (due to filtering/pagination)
} else {
// Remove the review property from the schema to keep it clean
// WARNING: This code assumes your schema is an ARRAY of products.
// If it's a single object, change this loop to: delete scriptTagObject.review;
for (var k = 0; k < scriptTagObject.length; k++) {
if (scriptTagObject[k] && scriptTagObject[k].review) {
delete scriptTagObject[k]['review'];
}
}
}
// Finally, update the schema tag on the page with the new content
scriptTag.innerHTML = JSON.stringify(scriptTagObject);
}
},
components: {
ReviewSnippet: 'pr-reviewsnippet',
ReviewDisplay: 'pr-reviewdisplay'
}
});
</script>