This question already has answers here:
What is the difference between client-side and server-side programming?
(3 answers)
Closed 9 months ago.
I am still in beginner's level of WordPress and PHP. I already doing my research in stackoverflow but because of my low-level understanding, I can't find the solution for the problem I have.
I use WordPress as CMS for my website. I use PHP echo for showing the average rating value of a user review plugin. It shows fine. But after a visitor insert his/her own rating and click submit button, the average rating value didn't update until the page is refreshed. How to automatically update the average rating value after the submit button clicked without having to refresh the page?
<script type="text/javascript">
var userRating = <?php echo rmp_get_avg_rating( $postID ); ?> ;
document.getElementById("div-rating").innerHTML = userRating;
</script>
This is the submit button class created by the plugin to trigger User Submission.
rmp-rating-widget__submit-btn rmp-btn js-submit-rating-btn rmp-rating-widget__submit-btn--visible
This is the PHP function I need to echo.
public function load_results() {
if ( wp_doing_ajax() ) {
// array with vote count, ratings and errors
$data = array(
'voteCount' => false,
'avgRating' => false,
'errorMsg' => '',
);
$post_id = intval( $_POST['postID'] );
$nonce = isset( $_POST['nonce'] ) ? $_POST['nonce'] : false;
// security check
if( ! $post_id ) {
$data['errorMsg'] = esc_html__( 'You cannot rate a web page without an id!', 'rate-my-post' );
echo json_encode( $data );
die();
}
$nonce_check = $this->has_valid_nonce( $nonce );
if( ! $nonce_check['valid'] ) {
$data['errorMsg'] = $nonce_check['error'];
echo json_encode( $data );
die();
}
$vote_count = rmp_get_vote_count( $post_id );
$sum_of_ratings = rmp_get_sum_of_ratings( $post_id );
$average_rating = rmp_calculate_average_rating( $sum_of_ratings, $vote_count );
if ( $average_rating ) { // post has been rated
$data['voteCount'] = $vote_count;
$data['avgRating'] = $average_rating;
} else { // no ratings so far
$data['voteCount'] = 0;
$data['avgRating'] = 0;
}
echo json_encode( $data );
};
die();
}
Thank you for your help.
Without refreshing the webpage, you can take the data on the server by using XMLHttpRequest object on client side. You can find a lot of implementations ( such as
XMLHttpRequest, Fetch API) which help you for communicating with the server from client side without refreshing page.
If you want to use a different technology ( Socket.io ), web sockets can meet your needs on this problem.
I use the Add to cart Ajax callback, but I miss how I can get the post Id there.
MY GOAL: I want to use the add_filter only on a specific page.
PHP in functions.php
add_filter( 'woocommerce_add_cart_item_data', 'iconic_add_engraving_text_to_cart_item' , 10, 3 );
function iconic_add_engraving_text_to_cart_item( $cart_item_data, $product_id, $variation_id ) {
global $post;
if ( $post->ID === 54214 ) {
$engraving_text = 'test';
$cart_item_data['iconic-engraving'] = $engraving_text;
return $cart_item_data;
} else {
return $cart_item_data;
}
}
This is NOT WORKING because $post is NULL (because of the Ajax woocommerce_add_cart_item_data hook).
So I tried the following code in the JS to get the post id in JS (working).
function get_current_page_id() {
var page_body = $('body.page');
var id = 0;
if(page_body) {
var classList = page_body.attr('class').split(/\s+/);
$.each(classList, function(index, item) {
if (item.indexOf('page-id') >= 0) {
var item_arr = item.split('-');
id = item_arr[item_arr.length -1];
return false;
}
});
}
return id;
}
Now, how can I handover the id to my Ajax Callback to work with it?
Any advice?
EDIT:
I forgot to tell that I am planning to use the add to cart action on other pages but not a single product page.
For that, I am using a third party plugin which gives me the button for my desired product, so I am not using default $product or $post Object (like in single product pages).
I have finally found a solution for this "simple" problem. I can work with a session:
add_action( 'wp_head', 'set_session' );
add_filter( 'woocommerce_add_cart_item_data', 'iconic_add_engraving_text_to_cart_item' , 10, 3 );
function set_session() {
session_start();
// add post id
global $post;
$post_id = $post->ID;
$_SESSION['post_id'] = $post_id;
}
function iconic_add_engraving_text_to_cart_item( $cart_item_data, $product_id, $variation_id ) {
session_start();
if( $_SESSION['post_id'] == 54214 ) {
$engraving_text = 'test';
$cart_item_data['iconic-engraving'] = $engraving_text;
return $cart_item_data;
} else {
return $cart_item_data;
}
}
I have created a shortcode which is just meant to simply return some text, and I put it in the functions.php file, but this just displays the text of the shortcode at the top of the homepage of my website. This is also my first custom shortcode so I would not be surprised if there are mistakes in it. I have checked answers to similar questions but none of them have helped. The code is:
function short_description_subtitle_shortcode( $atts ) {
$a = shortcode_atts( array(
'subtitle' => '<h4>Classes 1 to 14</h4><p></p>'
), $atts );
return $a['<h4>subtitle</h4><p></p>'];
}
add_shortcode( 'short_description_subtitle', 'short_description_subtitle_shortcode' );
How can I correct this?
Thank you
function short_description_subtitle_shortcode( $atts ) {
return "<h4>Classes 1 to 14</h4><p>" . $atts['val'] . "</p>";
add_shortcode( 'short_description_subtitle', 'short_description_subtitle_shortcode'
);
Then your shortcode will = [short_description_subtitle val = "2"]
I am trying to implement "redeem coupon" functionality in a Woocommerce store, I have already found useful tutorial, but I can't make it work properly.
This is the tutorial.
What I've already done:
Created new page template with this code:
<div class="redeem-coupon">
<form id="ajax-coupon-redeem">
<p>
<input type="text" name="coupon" id="coupon"/>
<input type="submit" name="redeem-coupon" value="Redeem Offer" />
</p>
<p class="result"></p>
</form><!-- #ajax-coupon-redeem -->
Added this to my theme's functions.php file:
add_action( 'wp_ajax_spyr_coupon_redeem_handler', 'spyr_coupon_redeem_handler' );
add_action( 'wp_ajax_nopriv_spyr_coupon_redeem_handler', 'spyr_coupon_redeem_handler' );
Added this to my theme's functions.php file:
function spyr_coupon_redeem_handler() {
// Get the value of the coupon code
$code = $_REQUEST['coupon_code'];
// Check coupon code to make sure is not empty
if( empty( $code ) || !isset( $code ) ) {
// Build our response
$response = array(
'result' => 'error',
'message' => 'Code text field can not be empty.'
);
header( 'Content-Type: application/json' );
echo json_encode( $response );
// Always exit when doing ajax
exit();
}
// Create an instance of WC_Coupon with our code
$coupon = new WC_Coupon( $code );
// Check coupon to make determine if its valid or not
if( ! $coupon->id && ! isset( $coupon->id ) ) {
// Build our response
$response = array(
'result' => 'error',
'message' => 'Invalid code entered. Please try again.'
);
header( 'Content-Type: application/json' );
echo json_encode( $response );
// Always exit when doing ajax
exit();
} else {
// Coupon must be valid so we must
// populate the cart with the attached products
foreach( $coupon->product_ids as $prod_id ) {
WC()->cart->add_to_cart( $prod_id );
}
// Build our response
$response = array(
'result' => 'success',
'href' => WC()->cart->get_cart_url()
);
header( 'Content-Type: application/json' );
echo json_encode( $response );
// Always exit when doing ajax
exit();
}
}
Created "kody.js":
jQuery( document ).ready( function() {
jQuery( '#ajax-coupon-redeem input[type="submit"]').click( function( ev ) {
// Get the coupon code
var code = jQuery( 'input#coupon').val();
// We are going to send this for processing
data = {
action: 'spyr_coupon_redeem_handler',
coupon_code: code
}
// Send it over to WordPress.
jQuery.post( woocommerce_params.ajax_url, data, function( returned_data ) {
if( returned_data.result == 'error' ) {
jQuery( 'p.result' ).html( returned_data.message );
} else {
// Hijack the browser and redirect user to cart page
window.location.href = returned_data.href;
}
})
// Prevent the form from submitting
ev.preventDefault();
});
});
Called the script from functions.php with this code:
function my_scripts_method() {
wp_register_script('kody',
get_template_directory_uri() . '/js/kody.js',
array('jquery'),
'1.0' );
enqueue the script
wp_enqueue_script('kody');
}
add_action('wp_enqueue_scripts', 'my_scripts_method');
And here's the weird thing: it's sort of working. I've setup a page where I can enter the coupon code, I paste the code, click "Redeem" and it adds products related to the coupon to the cart. It doesn't, however apply pre-defined discounts.
The "redeem coupon" page is also only half-working. It should display error messages when someone leaves the field empty or enters an incorrect code - and it only does the former. Entering incorrect code results in redirection to cart.
My knowledge on Ajax and JS is very limited, I tried to find some other tutorials, but without any luck.
Is something wrong with the code? It's from 2014, so something might have change in Wordpress engine, causing troubles.
Thank you in advance for any reply!
Regards
Problems solved, if anyone encounters similar issues with the tutorial provided, here's what you have to do:
To apply discount, add following code:
global $woocommerce;
WC()->cart->add_discount( $code );
Just below these lines:
// Coupon must be valid so we must
// populate the cart with the attached products
foreach( $coupon->product_ids as $prod_id ) {
WC()->cart->add_to_cart( $prod_id );
To display invalid code message change this:
// Check coupon to make determine if its valid or not
if( ! $coupon->id && ! isset( $coupon->id ) ) {
To this:
// Check coupon to make determine if its valid or not
if( ! $coupon->id && ! isset( $coupon_id ) ) {
Everything is working now.
(Also changed the title to make it easier to others to find this post in the future.)
I have various javascripts that are necessary plugins in one of my WordPress domains, and I know where in the php file it's called from.
I'm taking every measure I can take to speed up page loading times, and every speed tester on the web says to defer javascripts if possible.
I have read about the defer='defer' and the async functions in javascript and I think one of these will accomplish what I'm trying to accomplish. But I'm not understanding how I'd do so in a php file.
For instance, here is a snippet from one particular plugin's php file where the javascript file is being called:
function add_dcsnt_scripts() {
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'dcsnt', dc_jqsocialtabs::get_plugin_directory() . '/js/jquery.social.media.tabs.1.7.1.min.js' );
}
I've read that it's best to do something like this for faster page loading times:
<script defer async src="..."></script>
But I don't know how to accomplish that within a php file. I want to do this with all of my javascript files.
How would I accomplish deferring or asyncing this javascript snippet to is loads last and speeds up page load times? What would be the ideal way to increase page load times across all browsers? Thanks for any guidance anybody can offer!
Or more universal way:
function add_async_forscript($url)
{
if (strpos($url, '#asyncload')===false)
return $url;
else if (is_admin())
return str_replace('#asyncload', '', $url);
else
return str_replace('#asyncload', '', $url)."' async='async";
}
add_filter('clean_url', 'add_async_forscript', 11, 1);
so you can add async to any script without code changes, just add #asyncload to script url as:
wp_enqueue_script('dcsnt', '/js/jquery.social.media.tabs.1.7.1.min.js#asyncload' )
Trying to keep things somewhat modular and all encompassing, the following approach dynamically chooses how to embed the tag with the async or defer attributes by just appending a small identifier to the $handle name:
/**
* Add async or defer attributes to script enqueues
* #author Mike Kormendy
* #param String $tag The original enqueued <script src="...> tag
* #param String $handle The registered unique name of the script
* #return String $tag The modified <script async|defer src="...> tag
*/
// only on the front-end
if(!is_admin()) {
function add_asyncdefer_attribute($tag, $handle) {
// if the unique handle/name of the registered script has 'async' in it
if (strpos($handle, 'async') !== false) {
// return the tag with the async attribute
return str_replace( '<script ', '<script async ', $tag );
}
// if the unique handle/name of the registered script has 'defer' in it
else if (strpos($handle, 'defer') !== false) {
// return the tag with the defer attribute
return str_replace( '<script ', '<script defer ', $tag );
}
// otherwise skip
else {
return $tag;
}
}
add_filter('script_loader_tag', 'add_asyncdefer_attribute', 10, 2);
}
Example usage:
function enqueue_my_scripts() {
// script to load asynchronously
wp_register_script('firstscript-async', '//www.domain.com/somescript.js', '', 2, false);
wp_enqueue_script('firstscript-async');
// script to be deferred
wp_register_script('secondscript-defer', '//www.domain.com/otherscript.js', '', 2, false);
wp_enqueue_script('secondscript-defer');
// standard script embed
wp_register_script('thirdscript', '//www.domain.com/anotherscript.js', '', 2, false);
wp_enqueue_script('thirdscript');
}
add_action('wp_enqueue_scripts', 'enqueue_my_scripts', 9999);
Outputs:
<script async type='text/javascript' src='//www.domain.com/somescript.js'></script>
<script defer type='text/javascript' src='//www.domain.com/otherscript.js'></script>
<script type='text/javascript' src='//www.domain.com/anothercript.js'></script>
Thanks to #MattKeys #crissoca for inspiring my answer here.
This blog post links to two plugins of interest:
Asynchronous Javascript
Improve page load performance by asynchronously loading javascript using head.js
WP Deferred Javascripts
Defer the loading of all javascripts added with wp_enqueue_scripts, using LABJS (an asynchronous javascript library).
Haven't tested them but checked the code and they do pretty fancy stuff with WordPress scripts enqueuing process.
But then WPSE comes to rescue:
// Adapted from https://gist.github.com/toscho/1584783
add_filter( 'clean_url', function( $url ) {
if ( FALSE === strpos( $url, '.js' ) ) {
// not our file
return $url;
}
// Must be a ', not "!
return "$url' defer='defer";
}, 11, 1 );
Another solution using a different filter, which can be used to target a specific script handle:
function frontend_scripts()
{
wp_enqueue_script( 'my-unique-script-handle', 'path/to/my/script.js' );
}
add_action( 'wp_enqueue_scripts', 'frontend_script' );
function make_script_async( $tag, $handle, $src )
{
if ( 'my-unique-script-handle' != $handle ) {
return $tag;
}
return str_replace( '<script', '<script async', $tag );
}
add_filter( 'script_loader_tag', 'make_script_async', 10, 3 );
A simplified method. Add to your functions.php file to make make JavaScript asynchronous in Wordpress
// Make JavaScript Asynchronous in Wordpress
add_filter( 'script_loader_tag', function ( $tag, $handle ) {
if( is_admin() ) {
return $tag;
}
return str_replace( ' src', ' async src', $tag );
}, 10, 2 );
To gain control over which js files to defer and avoid conflicts you can append a variable to the url in the wp_register_script function like below.
wp_register_script( 'menu', get_template_directory_uri() . '/js/script.js?defer', array('jquery'), '1.0', true );
Then change the line:
if ( FALSE === strpos( $url, '.js' ))
To:
if ( FALSE === strpos( $url, '.js?defer' ))
The new filter looks like this.
add_filter( 'clean_url', function( $url )
{
if ( FALSE === strpos( $url, '.js?defer' ) )
{ // not our file
return $url;
}
// Must be a ', not "!
return "$url' defer='defer";
}, 11, 1 );
Very little modification code Mike Kormendy, which allows you to add 2 attributes at once:
// Async load
function add_asyncdefer_attribute($tag, $handle)
{
$param = '';
if ( strpos($handle, 'async') !== false ) $param = 'async ';
if ( strpos($handle, 'defer') !== false ) $param .= 'defer ';
if ( $param )
return str_replace('<script ', '<script ' . $param, $tag);
else
return $tag;
}
Result:
<script async defer type='text/javascript' src='#URL'></script>
I believe it's bad practice to defer/async WordPress jQuery. Better solution would be to exclude jQuery from the filter:
if (!is_admin()) {
add_filter( 'script_loader_tag', function ( $tag, $handle ) {
if ( strpos( $tag, "jquery.js" ) || strpos( $tag, "jquery-migrate.min.js") ) {
return $tag;
}
return str_replace( ' src', ' async src', $tag );
}, 10, 2 );
}
You can use defer instead of async
Incorporate the async attribute to the scripts so that they are only loaded once the whole page is loaded
<script type="text/javascript">
function ngfbJavascript( d, s, id, url ) {
var js, ngfb_js = d.getElementsByTagName( s )[0];
if ( d.getElementById( id ) ) return;
js = d.createElement( s );
js.id = id;
js.async = true;
js.src = url;
ngfb_js.parentNode.insertBefore( js, ngfb_js );
};
</script>
Source:Here
Something to add the clean_url filter solution, make sure to validate to use it only on the font-end maybe using if( ! is_admin() ){} popular plugins like ACF might give you a headache.
Update
Here is my modified version of the solution:
if( ! is_admin() ){
add_filter( 'clean_url', 'so_18944027_front_end_defer', 11, 1 );
function so_18944027_front_end_defer( $url ) {
if ( FALSE === strpos( $url, '.js' ) )
{ // not our file
return $url;
}
// Must be a ', not "!
return "$url' defer='defer";
}
}