fancybox icon indicating copy to clipboard operation
fancybox copied to clipboard

dynamically add/remove slides to existing instance

Open dsheyp opened this issue 8 years ago • 12 comments

hi

wouldn't it be a nice feature being able to add or remove slides to/from an existing instance of fancybox, something like:

var instance = $.fancybox.getInstance();
var opts = {
...
};
instance.appendSlides( $(".newFancyItems"), opts );
var posArr = [0, 3, 8, 9];
instance.removeSlides( posArr );

thanks dsheyp

dsheyp avatar Jun 10 '17 19:06 dsheyp

Hi,

When I was designing the api, I could not decide whether to implement such functionality or not. I could not figure out the use case where it would be needed, therefore it was not implemented. So, do you really need this and why? I think it would be weird when suddenly the group would change and navigation elements, thumbnails would appear or disappear.

fancyapps avatar Jun 12 '17 12:06 fancyapps

Hi,

i agree, that for the cases where i think fancybox is mostly used for (image galleries), it doesn't seem to be a very useful feature to add/remove slides, with thumbnails disappearing or the like.

i try to use fancybox as a general "overlay library". it allows me to show contextual content on an overlay layer while keeping a more aggregated view on the first layer. fancybox does this really well for my project. where i do have some problems is, when i want to switch from one contextual overlay to another. let's say a product detail page of my shop application is opened as an overlay over the general shop page, then i want to navigate to the shopping cart, which will also be opened as an overlay page. while i would "next/prev" in the product detail context like to navigate to the next/previous product detail, it is different in the cart context. here i would like that "next" navigates to a login or address page. on the way from the product detail page to the shopping cart i would like to keep fancybox open in order to have a fluent transition without opening/closing animations. so it would be useful for my case, if i was able to reconfigure the existing instance of fancybox with new slides and options. i almost achieved the add/remove functionality by extending fancybox. in order to do so, i use fancybox with only one slide and always set the content on beforeLoad. the only problem i have with that approach are transitions between slides, because i can only start to set the new content into the slide when transition ends and after that i can animate the slide with its new content. this makes no fluent transition between slides.

i would need at least two slides and alternate between those when setting new content - may be i'll try that later.

so much about my use case, that's basically why i was asking for the feature.

thanks for the great fancybox dsheyp

dsheyp avatar Jun 12 '17 13:06 dsheyp

Are you aware that you can open multiple instances simultaneously? From your description, I think that it would be better for you to simply open cart view/login form over the quick view overlay. Similarly to this example - http://fancyapps.com/fancybox/3/#ajax - just the other way. Or like here - https://codepen.io/fancyapps/pen/ZKqaKO - click on "Add to cart" button after opening an item.

fancyapps avatar Jun 12 '17 14:06 fancyapps

i am aware of the possibility of stacking multiple instances. i do this for modal dialogs (e.g. showing the terms & conditions) but i'd like to avoid stacking of instances for general navigation purposes in order to not generate a situation, where users have to close two contextual layers to return to the base content layer. however, if i don't manage to have fluent transitions between slides i'll may be have to rethink my decision of having only two content layers in the application...

my second reason for trying to stick to only one layer of contextual content has to do with the implementation of my app. when a page opens it checks for the presence of a fancybox instance and if it finds one, it loads itself into that instance instead of opening a new instance. having more than one instances of fancybox opened at the same time, means i'll have to implement some logic to decide into which instance a page has to load. i didn't think too much about that approach by now, so may be it could be easily done.

thanks dsheyp

dsheyp avatar Jun 12 '17 15:06 dsheyp

hi

the approach with two slides did work for my case, transitions are fluent now. from my side there's no need for add/remove slides functionality any more. if there's no broader requirement for this feature my request could be closed...

thanks dsheyp

dsheyp avatar Jun 13 '17 11:06 dsheyp

Hi Jānis!

First and foremost, we absolutely love the app, thank you!

I would like to +1 on the add / remove slides dynamically.

Background: Using the ajax modal with search results and pagination. 6 cards load, then 3 each time you scroll (infinite scroll).

Use case: Fire pagination on second to the last slide and update those in the gallery, so you could eternally swipe/scroll through all the posts on our site (10,000+).

Getting the index and firing the pagination is easy, but the gallery doesn't update. I was considering destroy() or close() and open() on the last slide, but that's just sloppy.

  • Jayson

ehus avatar Apr 17 '18 15:04 ehus

Hi, Thanks for your kind words!

Adding new content is quite simple using addContent method 😄 (available from v3.3). Example:

$.fancybox.getInstance().addContent({src:'https://source.unsplash.com/EMSDtjVHdQ8/1279x853'});

But removing is not implemented and is not planned for v3. I guess it is not so much needed anyway, because max 3 items are loaded at the same time

fancyapps avatar Apr 17 '18 15:04 fancyapps

Re-positing metafizzy/infinite-scroll#784 here

You can use fancybox's addContent with Infinite Scroll's append event. See demo https://codepen.io/desandro/pen/GdRzmL

var fancybox = $.fancybox.getInstance();

// with Infinite Scroll initialized in vanilla JS
infScroll.on( 'append', function( response, path, items ) {
  fancybox.addContent( items );
});
var fancybox = $.fancybox.getInstance();

// with Infinite Scroll initialized in jQuery
$container.on( 'append.infiniteScroll', function( event, response, path, items ) {
  fancybox.addContent( items );
});

desandro avatar Apr 18 '18 20:04 desandro

Thanks Jānis & David!

  • Jayson

ehus avatar Apr 18 '18 20:04 ehus

Hi Jānis,

I was wondering if you can help? I've been trying to dynamically add slides to an existing instance of Fancybox (using InfiniteScroll) but am encountering a weird issue where duplicate content starts appearing in Fancybox after the second InfiniteScroll page is reached.

I'm using Fancybox to check when approaching the last slide in the current group, then loading the next page of InfiniteScroll content (6 items per page) and appending to the Fancybox group:

Example code, using David's suggestion (@desandro) above:

// Fancybox
$().fancybox({
	selector: '[data-fancybox="images"]',
	loop: false,
	beforeShow: function(instance, current) {
	// When we reach the last item in current Fancybox instance, 
    	// load more images with Infinite Scroll and append them to Fancybox
        	if (current.index === instance.group.length - 1) { 
			// 1. Check if at end of group
			console.log('reaching end of current fancybox group'); // MARKER
					
            		// 2. Trigger infinite scroll to load next set of images
            		$container.infiniteScroll('loadNextPage');
			console.log('new infinite scroll page loaded'); // MARKER
					
            		// 3. Get the newly loaded set of images
            		$container.on( 'append.infiniteScroll', function( event, response, path, items ) {
				//4. And append to the Fancybox instance
  				instance.addContent( items );
				console.log('new scroll items added to fancybox'); // MARKER
				console.log( items ); // DIAGNOSTIC
			});
        	}
    	}
}); // end Fancybox

Here's what's happening in the console log (duplicate content marked with ***):

[Log] JQMIGRATE: Migrate is installed, version 1.4.1 (jquery-migrate.min.js, line 2)
[Log] [InfiniteScroll] pageIndex. current page determined to be: 1 from next link (infinite-scroll.pkgd.min.js, line 12)
[Log] [InfiniteScroll] initialized. on grid masonry-container masonry (infinite-scroll.pkgd.min.js, line 12)

[Log] reaching end of current infinite scroll group (collage, line 296)
[Log] [InfiniteScroll] request. URL: https://wordpress-153471-712046.cloudwaysapps.com/collage/page/2/ (infinite-scroll.pkgd.min.js, line 12)
[Log] new infinite scroll group loaded (collage, line 300)
[Log] [InfiniteScroll] load. Collage – Page 2 – Sally Ducrow. URL: https://wordpress-153471-712046.cloudwaysapps.com/collage/page/2/ (infinite-scroll.pkgd.min.js, line 12)
[Log] [InfiniteScroll] append. 6 items. URL: https://wordpress-153471-712046.cloudwaysapps.com/collage/page/2/ (infinite-scroll.pkgd.min.js, line 12)
[Log] new items added to fancybox (collage, line 306)
[Log] NodeList [<div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">] (6) (collage, line 307)

(All fine up to here. Then ...)

[Log] reaching end of current infinite scroll group (collage, line 296)
[Log] [InfiniteScroll] request. URL: https://wordpress-153471-712046.cloudwaysapps.com/collage/page/3/ (infinite-scroll.pkgd.min.js, line 12)
[Log] new infinite scroll group loaded (collage, line 300)
[Log] [InfiniteScroll] load. Collage – Page 3 – Sally Ducrow. URL: https://wordpress-153471-712046.cloudwaysapps.com/collage/page/3/ (infinite-scroll.pkgd.min.js, line 12)
[Log] [InfiniteScroll] append. 6 items. URL: https://wordpress-153471-712046.cloudwaysapps.com/collage/page/3/ (infinite-scroll.pkgd.min.js, line 12)
[Log] new items added to fancybox (collage, line 306)
***[Log] NodeList [<div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">] (6) (collage, line 307)
[Log] new items added to fancybox (collage, line 306)
***[Log] NodeList [<div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">] (6) (collage, line 307)

[Log] reaching end of current infinite scroll group (collage, line 296)
[Log] [InfiniteScroll] request. URL: https://wordpress-153471-712046.cloudwaysapps.com/collage/page/4/ (infinite-scroll.pkgd.min.js, line 12)
[Log] new infinite scroll group loaded (collage, line 300)
[Log] [InfiniteScroll] load. Collage – Page 4 – Sally Ducrow. URL: https://wordpress-153471-712046.cloudwaysapps.com/collage/page/4/ (infinite-scroll.pkgd.min.js, line 12)
[Log] [InfiniteScroll] append. 6 items. URL: https://wordpress-153471-712046.cloudwaysapps.com/collage/page/4/ (infinite-scroll.pkgd.min.js, line 12)
[Log] new items added to fancybox (collage, line 306)
***[Log] NodeList [<div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">] (6) (collage, line 307)
[Log] new items added to fancybox (collage, line 306)
***[Log] NodeList [<div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">] (6) (collage, line 307)
[Log] new items added to fancybox (collage, line 306)
***[Log] NodeList [<div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">, <div class="masonry-brick grid-cell sm-grid-1-1 md-grid-1-3">] (6) (collage, line 307)

And here's a link to the dev site: https://wordpress-153471-712046.cloudwaysapps.com/collage/

I've been working on this for several weeks, so would really appreciate any help you could offer.

I've also tried the following code, but am met with the same issue:

// Fancybox
$().fancybox({
    selector: '[data-fancybox="images"]',
    loop: false,
    beforeShow: function(instance, current) {
    	// When we reach the last item in current Fancybox instance, 
    	// load more images with Infinite Scroll and append them to Fancybox
        if (current.index === instance.group.length - 1) { 
		// 1. Check if at end of group
		console.log('reaching end of current fancybox group'); // MARKER
					
            	// 2. Trigger infinite scroll to load next set of images
            	$container.infiniteScroll('loadNextPage');
		console.log('new infinite scroll group loaded'); // MARKER
					
            	// 3. Get the newly loaded set of images
            	$container.on( 'load.infiniteScroll', function( event, response ) {
			console.log('add new images from infinite scroll to fancybox group'); // MARKER
                	var $posts = $(response).find('.masonry-brick');
                	// 4. Set up an array to put them in
                	var newImages = [];
                	$($posts).each( function( index, element ){
                    		// 5. Construct the objects
                    		var a = {};
                    		a['type'] = 'image';
                    		a['src'] = $(this).find('a').attr('href');
                    		// 6. Add them to the array
                    		newImages.push(a);
                	});
                	// 7. And append to the Fancybox instance
                	console.log(newImages); // DIAGNOSTIC
                	instance.addContent(newImages); // add images to fancybox
            	});
					
        }
    }
}); // end Fancybox

mujuw avatar Jan 04 '20 17:01 mujuw

Hi Jānis, thanks for all the hard work you've put into fancybox, really liking it so far and the docs are really helpful.

I've recently had to add the dynamic removal feature to an old codebase using fancybox. After adding a custom button and some experimentation, this worked:

var fancybox = null;

$('...').fancybox({
  onInit: function (instance) {
    fancybox = instance;
  }
});

$(document).on('click', '[data-fancybox-delete]', function () {
  var index = fancybox.current.index;
  fancybox.next();

  fancybox.group.splice(index, 1);
  delete fancybox.slides[index];

  // not sure if updating all these properties is necessary
  fancybox.current.index = index;
  fancybox.current.pos = index;
  fancybox.currIndex = index;
  fancybox.currPos = index;

  fancybox.updateControls();

  fancybox.group.forEach(function (slide, slideIndex) {
    slide.index = slideIndex;
  });
});

There are some additional checks specific to my project, but that's basically it. It would be great if fancybox had a removeSlide(index) method so I could get rid of this nasty code.

lkrhl avatar May 22 '20 08:05 lkrhl

Here's a more complete version of the removeSlide method, inspired from the code from @lkrhl :


$('...').fancybox({
  onInit: function (instance) {

    // Add the removeSlide method for the current instance
    instance.removeSlide = function(removeIndex) {

      if (instance.isClosing || instance.isAnimating || !instance.current) {
        return;
      }

      // No more items
      if (instance.group.length <= 1) {
        instance.close();
        return;
      }

      var curIndex = instance.current.index;
      var indexAdjust = -1;

      // console.log('remove index', removeIndex)
      // console.log('initial index', curIndex)

      // Load the next slide
      if (curIndex == removeIndex) {
        if (instance.next() === false) {
          instance.previous();
          indexAdjust = 0;
        }
      }

      curIndex = instance.current.index;

      // console.log('jump index', curIndex)

      var newIndex = (curIndex > 0 && curIndex >= removeIndex) ? (curIndex + indexAdjust) : curIndex;
      var transitionDuration = instance.current.opts.transitionDuration;
      var $thumbs = instance.Thumbs.$list.children();
      var $thumb = $thumbs.eq(removeIndex);
      $thumbs = $thumbs.not($thumb);

      // Remove instance from group
      instance.group.splice(removeIndex, 1);

      // Remake the group indices
      instance.group.forEach(function(slide, slideIndex) {
        slide.index = slideIndex;
        $thumbs.eq(slideIndex).attr('data-index', slideIndex)
        // Update the data-index from the the custom delete button above the thumb
        .find('.fb-remove-item').attr('data-index', slideIndex);
      });

      // Remove the slide
      if (instance.slides[removeIndex]) {
        instance.slides[removeIndex].$slide.remove();
        delete instance.slides[removeIndex];
      }

      var oldSlides = $.extend({}, instance.slides);
      instance.slides = {};

      // Remake the slides indices
      Object.keys(oldSlides).forEach(function(slideIndex) {
        if (slideIndex > removeIndex) {
          oldSlides[slideIndex].index = oldSlides[slideIndex].pos = slideIndex - 1;
          instance.slides[slideIndex - 1] = oldSlides[slideIndex];
        }
        else {
          instance.slides[slideIndex] = oldSlides[slideIndex];
        }
      });
      oldSlides = null;

      // Update instance indices
      instance.currIndex = newIndex;
      instance.currPos = newIndex;
      instance.prevIndex = 0;
      instance.prevPos = 0;

      // Update controls
      instance.updateControls();
      // Remake the slides
      instance.preload('image');

      // Remove the thumb with animation
      $thumb.fadeOut(transitionDuration, function() {
        $thumb.remove();
      });
    };
  }
});

bogdan-d avatar Aug 31 '20 19:08 bogdan-d