How to output image->caption() outside of a $image object?

Hello Forums,
I need to output the content of image->caption() in a different section of the page, which is outside of the foreach loop $image object.

I looked into the snippet doc page, but it wasn’t clear to me if it is possible to, say, store a $variable within a $variable.

Or what might be the way to accomplish this?


You can fetch a single image like this:

<?= $page->image('image-filename')->caption() ?>

But I'm not sure if that's what you mean. Could you pls. post your code and indicate where exactly in your code you want to output the image caption?

Yes, but I forgot to mention I have to fetch the captions from a slideshow, and display each caption text accordingly to the currently displayed image.

So, ideally, my code would be:

<div class="slideshow grid__item one-whole portable--four-sixths desk--four-sixths">
<?php foreach($page->images()->sortBy('sort', 'asc') as $image): ?>
      <a class="fancybox" href="<?php echo $image->url() ?>" data-title-id="<?php echo $image->name() ?>">
        <img src="<?php echo $image->url() ?>" width="<?php echo $image->width() ?>" height="<?php echo $image->height() ?>" alt="<?php echo $image->name() ?>" />
  <?php endforeach ?>

<!-- output -->

<div class="captions grid__item one-whole">
  <?php echo $image->caption() ?>

Yeah, but then I would expect that you display the caption below the image, i.e. within the foreach loop? Semantically, it would actually make sense to display the caption within the figure tag.

I agree! But, unfortunately, I need the caption to be outside.

I tried to store the caption variable into a php variable but I am no expert and from what I read it does not seem to be possible.

Any idea?

Even if you would pass the variable to the code outside the foreach loop, it doesn’t make sense, though, because PHP would loop through all the images and once that is done, it would then output a single image caption.

If you need all the image captions separately outside of the slideshow, you would need a second foreach loop.

Maybe you can push the captions into an array, using the first loop.

An re-call that array afterwards, outside the loop?

   $captions = array("Volvo", "BMW", "Toyota"); /* figure captions */

…and outside the first loop, you can iterate through the array $captions.

Of course this is something like a loop as well, but you don’t have to loop twice through all the assets (which is maybe a bit faster, since the array is in the memory already?).

Using an array would be an option, but I doubt that you would end up with what you want to achieve. The result would be a couple of figure tags, one after the other, and then AFTER those, just as many divs with titles.

Edit: Looks like you are using fancybox, so your output should actually look similar to this?

Yeah, you can regex them to filter the plain-text, but I don’t think that’s good usage :slight_smile:

Maybe one can enter the caption in the alt / title tag and read that, afterwards.

You can store meta-data for each image (set that in the blueprint) and do with it, whatever you want.

- note - this is for the openings-poster.

(Kirby Forum is really amazing, having replies on a Saturday evening—unbelievable! :smiley: )

with a second foreach loop I just have the full list of captions one after the other.

What I need is to have the slideshow (I’m using slick.js) to iterate over all the images while at the same time displaying each image caption next to it. All this on a different div, so outside the foreach loop, otherwise slick.js will ingurgitate them inside the slideshow as well.

If I keep it within the same foreach loop, slick.js will put it within the slideshow, as a new slide—there might be a way to avoid this but okay.

@texnixe, your suggestion of using fancybox to output the caption is very welcomed, but not what I need.

I was thinking of adding the caption field part of the image field to the alt/title tag, but I’ll fall into the same problem I encountered at the beginning—outside the $image object I will lose all the data of my pictures.

I’m gonna play a bit and will report back if I’ll find a way to make it work!

Thank you!

When you “store” the text / caption in a meta data file, you can push it to an array in the first loop - and re-use it afterwards on the same page.

I’ve done this lots of times (when re-using (meta) data at another point on the page).

Good luck! :slight_smile:

Can you instruct me on that? :open_mouth:

Is it similar to HTML5 data storage/attributes?

@afincato: Could you pls. post an example of what you would like your final HTML to look like? That would really make it a lot easier.

BTW. it seems that with slick.js you can also use the data-caption attribute …

@texnixe Thank you for that link! It was very helpful.


<div class="slideshow">
  <?php foreach($page->images()->sortBy('sort', 'asc') as $image): ?>
    <figure data-caption="<?php echo $image->caption() ?>">
        <img src="<?php echo $image->url() ?>" />
  <?php endforeach ?>

And js

$('.slideshow').on('init reInit afterChange', function(event, slick, currentSlide, nextSlide){
var image_caption = ($(slick.$slides.get(currentSlide)).attr('data-caption'));
var slick_current_slide = $('.slideshow').slick('slickCurrentSlide');


With this code though, while I can iterate over each slide and fetch its data-caption attribute, the appendTo function simply appends one after another each image caption.

I think I should use a jQuery each function, to print each image caption only when the related image is being displayed?

That’s why I asked what your final html markup should look like. Your captions div is a single div outside of the slider div, that’s why you end up with a div with all captions. That can’t possibly be correct. IMHO, your code should be something like this:

<div class="slideshow grid__item one-whole portable--four-sixths desk--four-sixths">
<?php foreach($page->images()->sortBy('sort', 'asc') as $image): ?>
      <a class="fancybox" href="<?php echo $image->url() ?>" data-title-id="<?php echo $image->name() ?>">
        <img src="<?php echo $image->url() ?>" width="<?php echo $image->width() ?>" height="<?php echo $image->height() ?>" alt="<?php echo $image->name() ?>" />
    <div class="captions grid__item one-whole">
     <?php echo $image->caption() ?>
  <?php endforeach ?>

Then you don't even need the data attrubute.

If I do that, slick.js makes new slides out of each image caption. And I don’t want that.

I feel like that storing the caption in a data-caption field and then output it outside the foreach loop based on the current slide is what I have to do. But as you made clear, as soon as I leave the slideshow’s foreach loop things get wrong—captions are accumulated.

Will try to thing of something else :slight_smile:

Unfortunately, the slick.js documentation is very limited as to what sort of markup is expected :frowning:

Edit: Maybe this codepen can help:

I know, eheh!

I found this other answer but it seems to be simply another version of the one I posted above, without the slick.js specific syntax (

$('.embedded-gallery .slick-slide > img').each(function(){ 
  if ($(this).attr('title')){
    var slideCaption = $(this).attr('title');
      $(this).parent('.slick-slide').append('<div class="slidecaption">' + slideCaption + '</div>');

What about just emptying the captions container before you append the next caption?
Adding $('.captions').html(""); before ($('<p>'+image_caption+'</p>').appendTo('.captions'); should do the trick.

BUM! it works perfectly!

Thank you so much :smiley: