Multiple Image Change on Click

Hi there,

I am currently trying to change my image source when clicking on the image, until the last image is shown.

In the page template, I defined these variables:

$mn = 0;
$first = $page->images()->nth($mn);

(I somehow tried to increase the $mn variable on click, but didn’t succeed)

this is the image I try to change:

<image id="first" src="<?= $first ->url() ?>"onclick="change_image()">

And I am using this script.

<script type='text/javascript'> function change_image { document.getElementById('first').setAttribute('src','<?= $first->next() ->url() ?>' ); </script> }

So this only helps me change the image to next listed one (of course), but at the moment I have no idea how to go further.

Welcome to the community :slight_smile:

What you need is an array of all the images that you can step through with the click event… something like this in your template.

<?php

// Empty array for our images
$images = [];

// Get the urls and wrap each one in single qoutes
foreach ($page->images() as $img) {
  $images[] = "'". $img->url() . "'";
}

// Make the comma seperated list that javascript can read
$images = implode(', ', $images)

?>

<script>

function ImageCollection(images) {
  this.images = images;
  this.i = 0;
  this.next = function(img) {
    this.i++;
    if (this.i == images.length) this.i = 0;
    img.src = images[this.i];
  };
}

// Use the list of images created above
var imgSet = new ImageCollection([<?= $images ?>]);

</script>


<img src='<?= $page->images()->first()->url() ?>' onclick="imgSet.next(this)">

You say you want it to stop on the last image, however I would argue that that is a poor user experience, since you would need to reload the page and cycle through again to see a picture you went past again. The code above cycles through infinitly.

1 Like

Amazing, thank you so much for the help! :parrot:

Something else should happen when all the images are shown, but for now your loop is great!

No worries. You can make the code above safer by wraping the whole thing in hasImages(). It is good practice to check things exist before trying to use them.

<?php if($page->hasImages()) :

  // Empty array for our images
  $images = [];

  // Get the urls and wrap each one in single qoutes
  foreach ($page->images() as $img) {
    $images[] = "'". $img->url() . "'";
  }

  // Make the comma seperated list that javascript can read
  $images = implode(', ', $images)

?>

<script>

function ImageCollection(images) {
  this.images = images;
  this.i = 0;
  this.next = function(img) {
    this.i++;
    if (this.i == images.length) this.i = 0;
    img.src = images[this.i];
  };
}

// Use the list of images created above
var imgSet = new ImageCollection([<?= $images ?>]);

</script>


<img src='<?= $page->images()->first()->url() ?>' onclick="imgSet.next(this)">

<?php endif ?>
1 Like

Good hint, need to do things like that in literally every line of my code but tried to procrastinate till now :upside_down_face:

Hello I know this is quite old but I’m doing this same thing but trying to also update and display the metadata when the image src is changed. Any idea how to apply similar logic to “grab” the image metadata?

Thanks!

Yes @juliaedyck , something like this will do it…

<?php if($page->hasImages()) :

// Empty array for our images
$images = [];

// Get the urls, alt and titles
foreach ($page->images() as $img) {
  $images[] = [
    'url' => $img->url(),
    'alt' =>  ($img->alt()->isNotEmpty()) ? $img->alt()->value() : ' ',  
    'title' =>  ($img->title()->isNotEmpty()) ? $img->title()->value() : ' '
  ];
}
// get the first image for on page load
$firstImg = $page->images()->first(); 
?>


<img id='flick' src='<?= $firstImg->url() ?>' alt='<?= $firstImg->alt() ?>' title='<?= $firstImg->title() ?>' onclick='nextImg()'>

<script>
  let imgArray = <?php echo json_encode($images); ?>;
  let myIndex = 1;
  let img = document.getElementById('flick');

  function nextImg() {
    img.src = imgArray[myIndex].url;
    img.alt = imgArray[myIndex].alt;
    img.title = imgArray[myIndex].title;
    myIndex = (myIndex+1)%(imgArray.length);
  };
</script>

<?php endif ?>

Hi! Thank you so much! This does work but if I try to render $firstImg->title() on the page it doesn’t update from the initial first image info, even though the change is made in the actual image metadata (as I can see in the inspector).

well i dont know how u have set up your blueprint keys. You will need tweak the above a bit to match what you have actually called the alt and titile fields in the file meta.

thank you! yes the blueprints and alt, title etc fields are corresponding but even if I render $firstImg->url() for example it still gives me the url of the first image even when the image has changed. Is there another way to fetch the file that will give me the updated data?

Not sure what you mean there, It’s working fine for me in my project, and all three attributes change on click.

Maybe you have an error in the code some where? $firstImg is just for when the page loads to populate the attributes before a click happens.

The HTML will always remain the same if you do a view source in the browser, since its being minupulated dynamically by Javascript.

Can confirm that the code does what it should, provided the fields exist in the meta data.

Ok I must be making a silly error so I’m copying my code here. The div class=‘credits’ is where I have the issue. I have tried fetching the data multiple ways but it never changes from the first image’s meta-data when the image changes. However in the div class=‘gallery’ the metadata displays correctly. Thank you for your help!

<main class="project">

  <article>

    <?php

    // Empty array for our images
    $images = [];

    // Get the urls, alt and titles
    foreach ($page->images() as $img) {
      $images[] = [
        'url' => $img->url(),
        'alt' => ($img->alt()->isNotEmpty()) ? $img->alt()->value() : ' ',
        'title' => ($img->title()->isNotEmpty()) ? $img->title()->value() : ' ',
        'technique' => ($img->technique()->isNotEmpty()) ? $img->technique()->value() : ' ',
        'year' => ($img->year()->isNotEmpty()) ? $img->year()->value() : ' ',
        'collection' => ($img->collection()->isNotEmpty()) ? $img->collection()->value() : ' '

      ];
    }

    ?>

 
    <div class="project-cover">
      <figure>
        <?php $firstImg = $page->images()->first() ?>
        <img id='flick' src='<?= $firstImg->resize(1200, 1000)->url() ?>' alt='<?= $firstImg->alt() ?>' title='<?= $firstImg->title() ?>' technique="<?= $firstImg->technique() ?>" year="<?= $firstImg->year() ?>" collection="<?= $firstImg->collection() ?>" onclick='nextImg()'>
        <div class="credits">

          <p><?= $firstImg ->title() ?></p>
          <p><?= $firstImg->technique() ?></p>
          <p><?= $firstImg->year() ?></p>
          <p><?= $firstImg->collection() ?></p>


        </div>
      </figure>
    </div>



    <div class="gallery">
      <?php if ($gallery = $page->images()->sortBy('sort', 'asc')) : ?>
        <ul class="project-gallery">
          <?php foreach ($gallery as $index => $image) : ?>
            <li title="<?= $image->title() ?>" technique="<?= $image->technique() ?>" year="<?= $image->year() ?>" collection="<?= $image->collection() ?>">

              <img src="<?= $image->resize(1200, 1000)->url() ?>" alt="<?= $image->alt() ?>" class="gallery-image" title="<?= $image->title() ?>" data-technique="<?= $image->technique() ?>" data-year="<?= $image->year() ?>" data-collection="<?= $image->collection() ?>" />
            </li>


            <div>
              <p id="title"><?= $image->title() ?></p>
              <p id="technique"><?= $image->technique() ?></p>
              <p id="year"><?= $image->year() ?></p>
              <p id="collection"><?= $image->collection() ?></p>
            </div>
          <?php endforeach ?>
        </ul>
      <?php endif ?>
    </div>

  </article>

</main>


<script>
  let imgArray = <?php echo json_encode($images); ?>;
  let myIndex = 1;
  let img = document.getElementById('flick');

  function nextImg() {
    img.src = imgArray[myIndex].url;
    img.alt = imgArray[myIndex].alt;
    img.title = imgArray[myIndex].title;
    img.technique = imgArray[myIndex].technique;
    img.year = imgArray[myIndex].year;
    img.collection = imgArray[myIndex].collection;
    myIndex = (myIndex + 1) % (imgArray.length);

  };

</script>

I don’t see any code that would update the strings in the credits div…

Ok, so you have a different HTML stucture there. To change those paragraphs in the credits div to have unique IDs

<p id="ctitle"><?= $firstImg->title() ?></p>
<p id="ctechnique"><?= $firstImg->technique() ?></p>
<p id="cyear"><?= $firstImg->year() ?></p>
<p id="ccollection"><?= $firstImg->collection() ?></p>

And then you should be able to change them on click with a change to the script:

<script>
  let imgArray = <?php echo json_encode($images); ?>;
  let myIndex = 1;
  let img = document.getElementById('flick');
  let ctitle = document.getElementById('ctitle');
  let cctechnique = document.getElementById('cctechnique');
  let cyear = document.getElementById('cyear');
  let ccollection = document.getElementById('ccollection');

  function nextImg() {
    img.src = imgArray[myIndex].url;
    img.alt = imgArray[myIndex].alt;
    img.title = imgArray[myIndex].title;

    ctitle.innerHTML = imgArray[myIndex].title;
    cctechnique.innerHTML = imgArray[myIndex].technique;
    cyear.innerHTML = imgArray[myIndex].year;
    ccollection.innerHTML = imgArray[myIndex].collection;
    
    myIndex = (myIndex + 1) % (imgArray.length);

  };

</script>

This works! Thank you so much!!