Link to home
Start Free TrialLog in
Avatar of Member_2_7966984
Member_2_7966984

asked on

Use PHP Loop to output values inside a JavaScript

So I have a Java Script I am using to pre-load heavier gif files after the page loads 


I have got it working by manually inputting the the data into the JavaScript - however I would like to use a PHP loop, to output the  the values dynamically from a custom post type. 


This is the post data I use to drill down into the custom post type and custom post type tax I am trying to pull from .. 


<?php
   $args = array( 
              'numberposts' => 100, 
              'post_type' => 'rps-portfolio',
              'tax_query' => array(
                  array(
                    'taxonomy' => 'portfolio-type',
                    'field' => 'id',
                    'terms' => 438
                  )
                 )
      );
   $myposts = get_posts( $args );
   //var_dump($myposts);
   foreach( $myposts as $post ) :   setup_postdata($post);
   $animated_gif = get_post_meta($post->ID, 'wpcf-animated-gif', true);
   ?> 

Open in new window

and this is how I have pulled in the animated gif from the custom field in another loop I have used... So this is the name of the custom field to be output as the the URL. 


<?= get_post_meta($post->ID, "wpcf-animated-gif", true); ?>

Open in new window


So there are a couple of values in the the JavaScript that need to increment by 1 as the loop processes each piece of data... 


Here is a screen shot showing the dynamic parts that need to be output in the loop and this is also the desired output after the loop runs 


User generated image


Here is my base JavaScript


<script type="text/javascript">function preloader() {
   if (document.images) {
      // PHP Loop Code to recreate this structure Needed  
      
      var img1 = new Image();
      var img2 = new Image();
      var img3 = new Image();

      img1.src = "/wp-content/uploads/2022/08/candle-lighters.gif";
      img2.src = "/wp-content/uploads/2022/08/mama-gerties.gif";
      img3.src = "/wp-content/uploads/2022/08/artemis-website-gif.gif";
   }
}
function addLoadEvent(func) {
   var oldonload = window.onload;
   if (typeof window.onload != 'function') {
      window.onload = func;
   } else {
      window.onload = function() {
         if (oldonload) {
            oldonload();
         }
         func();
      }
   }
}
addLoadEvent(preloader);
</script> 

Open in new window


So looking for a way I can possibly query my custom field and dynamically output the data in the JavaScript using a loop that increments the variable name by 1 as the loop is processed in the two spots outlined in red on the screen shot and and then output the url of the gif ( <?= get_post_meta($post->ID, "wpcf-animated-gif", true); ?> ) also outlined in red on the screen shot .


 



Avatar of Chris Stanyon
Chris Stanyon
Flag of United Kingdom of Great Britain and Northern Ireland image

Hey there,

Probably the most 'wordpress' way to do it is to call the wp_add_inline_script() function - either from your own custom plugin or from your themes function.php file. Basically, you enqueue your external JS file and then you add the inline JS code. General idea is something along these lines:

add_action('wp_enqueue_scripts', function() {

  // Grab your PHP data here and create an array, for example
  $images = [
    "/wp-content/uploads/2022/08/candle-lighters.gif",
    "/wp-content/uploads/2022/08/mama-gerties.gif",
    "/wp-content/uploads/2022/08/artemis-website-gif.gif",
  ];

  // Create the JS you want injecting
  $js = "const MY_IMAGES = " . json_encode($images);
 
  // enqueue your main JS file 
  wp_enqueue_script('myscript_handle', get_template_directory_uri() .'/js/yourscript.js');

  // and inject your PHP data
  wp_add_inline_script('myscript_handle', $js, 'before');

});

Open in new window

You'll need to edit your JS a little because what the above code does is create a variable within Javascript called MY_IMAGES . That will be an array of your images, and because its in JS, you can just loop over it:

MY_IMAGES.forEach(img => {
  // deal with each img in here ...
})

Open in new window

You JavaScript is from the mid 90s. Here is an updated version
https://jsfiddle.net/mplungjan/Lbp1qtxc/

// this is a simple JS array. You ONLY need the PHP to update this
// $animated_gifs is a PHP array of your images
const sources = <?= json_encode($animated_gifs) ?>;
// here is the rest of the code
let cnt = 0;
const preload = () => {
  if (cnt >= sources.length) return; // stop
  const img = new Image()
  img.addEventListener("load", () => {
    cnt++;
    preload();
  });
  img.addEventListener("error", () => {
    console.log("Failed to load", sources[cnt])
    cnt++;
    preload();
  });
  img.src = sources[cnt];
};

window.addEventListener("DOMContentLoaded", preload);

Open in new window

Avatar of Member_2_7966984
Member_2_7966984

ASKER

Chris When you say "Grab Your PHP Data here" what exactly are you referring to ?

is this what you are referring to?

<?php
   $args = array( 
              'numberposts' => 100, 
              'post_type' => 'rps-portfolio',
              'tax_query' => array(
                  array(
                    'taxonomy' => 'portfolio-type',
                    'field' => 'id',
                    'terms' => 438
                  )
                 )
      );
   $myposts = get_posts( $args );
   //var_dump($myposts);
   foreach( $myposts as $post ) :   setup_postdata($post);
   $animated_gif = get_post_meta($post->ID, 'wpcf-animated-gif', true);
   ?> 
   

Open in new window


in your example it looks like you have hard coded the image paths - but I need to pull the image paths in dynamically via a loop. from a custom field and then increment the variable name by one . 
According to the internets, here is how you can make the array I used in my javascript above

<? $animated_gifs = array();
foreach($myposts as $post) {
  setup_postdata($post);
  $animated_gifs[] = get_post_meta($post->ID, 'wpcf-animated-gif', true);
}?>
const sources = <?= json_encode($animated_gifs) ?>;

Open in new window

Yeah - that's exactly what I'm referring to. Basically, within the function, you would need to run whatever PHP you need to grab the data and then create your array from that. I only hard coded as an example. Something like this (untested):

add_action('wp_enqueue_scripts', function() {

  $images = [];

  $args = [ 
    'numberposts' => 100, 
    'post_type' => 'rps-portfolio',
    'tax_query' => [
      'taxonomy' => 'portfolio-type',
      'field' => 'id',
      'terms' => 438
    ]
  ];

  $myposts = get_posts( $args );
  foreach( $myposts as $post ):  
    setup_postdata($post);
    $images[] = get_post_meta($post->ID, 'wpcf-animated-gif', true);
  endforeach;

  $js = "const MY_IMAGES = " . json_encode($images);
 
  // enqueue your main JS file 
  wp_enqueue_script('myscript_handle', get_template_directory_uri() .'/js/yourscript.js');

  // and inject your PHP data
  wp_add_inline_script('myscript_handle', $js, 'before');

}

Open in new window

 
Chris Is it possible to do something like this... of course this doesn't work due to my limited php skills but would it be possible to convert this Idea into a working solution?

User generated image

May I ask if you even consider looking at my suggestions or am I wasting my time trying to help you?
Not entirely sure why you specifically want to create your variables like that. If you're just wanting to preload, then it doesn't need to be done like that. Something like this would do the same job:

if (document.images) {
  const preload = [];

  MY_IMAGES.forEach(img => {
    preload.push( (new Image()).src = img);
  });
}

Open in new window


@Michel - this is a Wordpress issue, so personally, I'd advise against echoing out PHP in an inline JS file. For WP, using wp_add_inline_script() is the best-practice approach to it.
So I was trying to get the images to preload after the page loaded - I did not want to block rendering of the page while the large animated gifs downloaded the animated gifs only displayed on mouse over - so without the preload there would be a significant pause before the animated gif would display on mouseover

 - so the goal in a nutshell was to load the page as fast as possible and then AFTER the page loaded go ahead and start downloading the large animated mouse gif images to make the mouse over image appear faster without the delay

I was able to get the initial solution to work and do all that.... however I wanted it to be dynamic and pull in the image paths from a custom field... So since I had a working hard coded solution - my challenge was to pull in the pieces and dynamically write out the code.

This was the working Solution I ended up with thanks to every ones help

<script>
<?
  $images = [];


   $args = array(
              'numberposts' => 100,
              'post_type' => 'rps-portfolio',
              'tax_query' => array(
                  array(
                    'taxonomy' => 'portfolio-type',
                    'field' => 'id',
                    'terms' => 438
                  )
                 )
      );
$counter=0;
  $myposts = get_posts( $args );
  foreach( $myposts as $post ):  
    setup_postdata($post);
    $counter++;
   echo "var img".$counter." = new Image();\n";
   echo "img".$counter.".src = '". get_post_meta($post->ID, 'wpcf-animated-gif', true) ."';\n";
  endforeach;
      
?>   
      
   
   }
}
function addLoadEvent(func) {
   var oldonload = window.onload;
   if (typeof window.onload != 'function') {
      window.onload = func;
   } else {
      window.onload = function() {
         if (oldonload) {
            oldonload();
         }
         func();
      }
   }
}
addLoadEvent(preloader);
</script>

Open in new window


  
Michel Plungjan It isn't that I did not look at your suggestion - I just do not understand how to apply it to my php loop and pull in the data dynamically with your suggestion - now that I have the php sorted out and working

I would be open to knowing how I might adapt this to work with your more modern suggestion.  
Hmmm. If that solution works for you, then go with it, but it's NOT really the right way to do it in the WordPress eco-system.

The solution I showed  would do exactly what you wanted, but it would use the recommended Wordpress way of doing things.  
If you can create the array and insert it using Wordpress methods, you should certainly go for it. I still recommend you use my script or another newer script to process that array since your current script is LITERALLY from the 1990s  - the test for document image array is a test for Internet Explorer 3.2
NOTE: If you preload animated gifs, they may not play again
Michel - I have implemented your solution it looked like this

<script type="text/javascript">
   
const sources = [<?
  $images = [];


   $args = array(
              'numberposts' => 12,
              'post_type' => 'rps-portfolio',
              'tax_query' => array(
                  array(
                    'taxonomy' => 'portfolio-type',
                    'field' => 'id',
                    'terms' => 438
                  )
                 )
      );
   
 $myposts = get_posts( $args );
  foreach( $myposts as $post ):  
    setup_postdata($post);
   echo "'". get_post_meta($post->ID, 'wpcf-animated-gif', true) ."',\n";
  endforeach;
?>
];


let cnt = 0;
const preload = () => {
  if (cnt >= sources.length) return; // stop
  const img = new Image()
  img.addEventListener("load", () => {
    cnt++;
    preload();
  });
  img.addEventListener("error", () => {
    console.log("Failed to load", sources[cnt])
    cnt++;
    preload();
  });
  img.src = sources[cnt];
};


window.addEventListener("DOMContentLoaded", preload);
</script>

Open in new window

It seems to work, just one question

- Does this do the pre loading of the animated gifs after the page loads?
or
Does this start preloading the images as soon as the page loads?

 - ideally I would like the page to load first - and then begin preloading the images after the page loads - if this doesn't work that way what would we need to update in order to make it work that way?

This is what the current output looks like when I view the page source - Does this look correct?
User generated image


 I was originally using this method shown here https://perishablepress.com/3-ways-preload-images-css-javascript-ajax/  that you said was old  (Method 2: Preloading with JavaScript Only - JavaScript Method #2 ) because of this explanation -  

We can even improve this method a bit by delaying preloading until after the page loads. To do this, we simply wrap the script in a function and use addLoadEvent() to make it work:
   
This statement tells the browser to start preloading when the page has completed loading

window.addEventListener("DOMContentLoaded", preload);

It is more or less the same as INSTEAD use preload() at the bottom of the page depending on how many external files you load
<script>
preload()
</script>
</body>

Open in new window

Can we drop the empty first entry? If not we need to filter:
const sources = [
....
....

?>
].filter(img => img &&  img.trim() !== "");

Open in new window


Ok so it sounds like I do not need to modify any thing to kick the preloading in AFTER the page loads this because of this line

window.addEventListener("DOMContentLoaded", preload);

Is that correct?

So some of the entries will have animated gifs and some will not - so the ones that do not might output the empty entry 
This is the page where I have implemented this solution - (carousel at the bottom)

https://rpsstage.wpengine.com/our-work/
That page doesn't have the new code - it still seems to have the old var img1 = "..." code, so maybe you've still got an old version online.

You do realise that you have jQuery available to you on your site, so the whole notion of DOMContentLoaded event listeners is not necssary. Just wrap your code in the jQuery Dom Ready block - it'll do the same thing

Chris might be cached on the server -0 I cleared the server's cache


This is what should appear in the view source -

<script type="text/javascript">
   
const sources = ['',
'https://rpsstage.wpengine.com/wp-content/uploads/2022/08/candle-lighters.gif',
'https://rpsstage.wpengine.com/wp-content/uploads/2022/08/artemis-website-gif.gif',
'https://rpsstage.wpengine.com/wp-content/uploads/2022/08/mama-gerties.gif',
].filter(img => img &&  img.trim() !== "");


let cnt = 0;
const preload = () => {
  if (cnt >= sources.length) return; // stop
  const img = new Image()
  img.addEventListener("load", () => {
    cnt++;
    preload();
  });
  img.addEventListener("error", () => {
    console.log("Failed to load", sources[cnt])
    cnt++;
    preload();
  });
  img.src = sources[cnt];
};


window.addEventListener("DOMContentLoaded", preload);
</script>

Open in new window

For jQuery we can do

$( window ).on( "load", preload)

Open in new window


instead of

window.addEventListener("DOMContentLoaded", preload);

Open in new window

normally when I need to use jQuery in WordPress I have to wrap it with something like this


<script>
jQuery(document).ready(function($) {
   // Code that uses jQuery's $ can follow here.
   
   
   });
</script>

Open in new window


so would it then look like?

jQuery(document).ready(function($) {
   // Code that uses jQuery's $ can follow here.
   $( window ).on( "load", preload)
   
   });

Open in new window

I see some path issues when mousing over the images and they are not animated either as I suspected they might not be because they already played when preloaded.
This is WordPress, so you shouldn't use the $ at the root. This is the correct DomReady call:

jQuery(function($) {
  // Code goes here and you can use the $ in here :)
  ...
});

Open in new window

not all of them have animated gifs -
User generated image

Sure you can do

$(function() {
  something
  something
  preload()
})

but

$( window ).on( "load", preload)

Open in new window


or

jQuery( window ).on( "load", preload)

Open in new window


does the same and can be placed anywhere after the jQuery library but NOT in another load/ready function
So is this how it would look using jquery?

<script type="text/javascript">
   
   
jQuery(document).ready(function($) {
   // Code that uses jQuery's $ can follow here.   
   
   
const sources = [<?
  $images = [];

   $args = array( 
              'numberposts' => 12, 
              'post_type' => 'rps-portfolio',
              'tax_query' => array(
                  array(
                    'taxonomy' => 'portfolio-type',
                    'field' => 'id',
                    'terms' => 438
                  )
                 )
      );
   
 $myposts = get_posts( $args );
  foreach( $myposts as $post ):  
    setup_postdata($post);
   echo "'". get_post_meta($post->ID, 'wpcf-animated-gif', true) ."',\n"; 
  endforeach;
?>
].filter(img => img &&  img.trim() !== "");

let cnt = 0;
const preload = () => {
  if (cnt >= sources.length) return; // stop
  const img = new Image()
  img.addEventListener("load", () => {
    cnt++;
    preload();
  });
  img.addEventListener("error", () => {
    console.log("Failed to load", sources[cnt])
    cnt++;
    preload();
  });
  img.src = sources[cnt];
};


$( window ).on( "load", preload)

   
     });
</script>

Open in new window

No.

EITHER  change it to just

preload()

OR move

jQuery( window ).on( "load", preload);  

outside the });
ASKER CERTIFIED SOLUTION
Avatar of Member_2_7966984
Member_2_7966984

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Yep - that'll do it

very  helpful 
So turns out I have one other carousel on that page I need to preload images for ... So wondering how I might adapt this to loop through 2 taxonomy terms I tried this but it only looped through 1 term ID not both term IDs

<script type="text/javascript">
      jQuery(document).ready(function(){
const sources = [<?
  $images = [];


   $args = array(
              'numberposts' => 12,
              'post_type' => 'rps-portfolio',
              'tax_query' => array(
             'relation' => 'OR',
                  array(
                    'taxonomy' => 'portfolio-type',
                    'field' => 'term_id',
                    'terms' => 403
                  ),
                  array(
                   'relation' => 'AND',
                  'taxonomy' => 'portfolio-type',
                  'field' => 'term_id',
                  'terms' => 438
                  )
           )
      );
   
 $myposts = get_posts( $args );
  foreach( $myposts as $post ):  
    setup_postdata($post);
   echo "'". get_post_meta($post->ID, 'wpcf-animated-gif', true) ."',\n";
  endforeach;
?>
].filter(img => img &&  img.trim() !== "");


let cnt = 0;
const preload = () => {
  if (cnt >= sources.length) return; // stop
  const img = new Image()
  img.addEventListener("load", () => {
    cnt++;
    preload();
  });
  img.addEventListener("error", () => {
    console.log("Failed to load", sources[cnt])
    cnt++;
    preload();
  });
  img.src = sources[cnt];
};








preload()




   
     });
</script>

Open in new window