Rearrange the order of items in the fancybox gallery

Typically, fancybox displays items in the gallery based on the order they are added in the HTML code.

Is there a way to customize the order of items when they are opened in the popup, while keeping the original order when displayed on the page?

The solution involves adding a optional data- attribute to each item:

<a class="fancybox" rel="gallery1" href="http://farm2.staticflickr.com/1617/24108587812_6c9825d0da_b.jpg" title="Morning Godafoss (Brads5)">
    <img src="http://farm2.staticflickr.com/1617/24108587812_6c9825d0da_m.jpg" alt="" />
</a>
<a class="fancybox" rel="gallery1" href="http://farm4.staticflickr.com/3691/10185053775_701272da37_b.jpg" title="Vertical - Special Edition! (cedarsphoto)">
    <img src="http://farm4.staticflickr.com/3691/10185053775_701272da37_m.jpg" alt="" />
</a>
<a class="fancybox" rel="gallery1" href="http://farm1.staticflickr.com/574/22407305427_69cc6e845f_b.jpg" title="Racing against the Protons (tom.leuzi)">
    <img src="http://farm1.staticflickr.com/574/22407305427_69cc6e845f_m.jpg" alt="" />
</a>
<a class="fancybox" rel="gallery1" href="http://farm1.staticflickr.com/291/18653638823_a86b58523c_b.jpg" title="Lupines (Kiddi Einars)">
    <img src="http://farm1.staticflickr.com/291/18653638823_a86b58523c_m.jpg" alt="" />
</a>

JavaScript code for fancybox initialization:

$(".fancybox").fancybox();

JSFiddle

If you want to open the images in the popup in a different order than they appear on the page, you can use the following setup:

Specify the desired order using the data-fancybox-order attribute like this:

<a class="fancybox" rel="gallery1" data-fancybox-order="3" ... >
<a class="fancybox" rel="gallery1" data-fancybox-order="1" ... >
<a class="fancybox" rel="gallery1" data-fancybox-order="4" ... >
<a class="fancybox" rel="gallery1" data-fancybox-order="2" ... >

With this setup, navigation buttons will adjust based on the specified order. For example, if the second image is clicked (data-fancybox-order="1"), there will be no previous button. Clicking next will open the fourth image, then the first, and finally the third image (data-fancybox-order="4"), after which the next button will disappear.

What's the best approach to implement this?

Answer №1

Regrettably, fancybox does not provide an option for that specific feature. It may be necessary to manually create and initiate the fancybox gallery in the desired sequence.

To achieve this, you must assign a data attribute to each html element to specify the display order within the fancybox gallery. For instance:

<a class="fancybox" href="image03.jpg" title="title 3" data-order="3">...
<a class="fancybox" href="image01.jpg" title="title 1" data-order="1">...
<a class="fancybox" href="image04.jpg" title="title 4" data-order="4">...
<a class="fancybox" href="image02.jpg" title="title 2" data-order="2">...

As the gallery will be constructed programmatically, the rel attribute becomes redundant.

Instead of utilizing the standard fancybox initialization script $(".fancybox").fancybox(), you need to bind a click event to dynamically create and launch the fancybox gallery:

$(".fancybox").click(function(){
    // establish the fancybox gallery here
    return false; // prevent default event behavior and propagation
});

Upon clicking any of the html thumbnails, the process involves:

  • obtaining the index of the clicked image (data-order attribute)
  • collecting all html elements with class fancybox (querySelectorAll())
  • initializing the array of elements for the fancybox gallery
  • iterating through the html elements and adding them to the fancybox gallery array
  • sorting the fancybox array

Since the fancybox gallery object will consist of an array of objects, a function is required to sort such an array based on a chosen property. This function can be sourced from this answer:

// function to sort array of objects by a property
var sortObjectsBy = function(field, reverse, primer) {
  var key = primer ? function(x) {
    return primer(x[field])
  } : function(x) {
    return x[field]
  };
  reverse = !reverse ? 1 : -1;
  return function(a, b) {
    return a = key(a),
      b = key(b),
      reverse * ((a > b) - (b > a));
  }
}

Subsequently, all components are integrated after the click event:

$(".fancybox").on("click", function() {
  // retrieve the index of the clicked image
  var thisIndex = this.dataset.order - 1;
  // gather all html elements
  var elements = document.querySelectorAll(".fancybox");
  // initialize the array for the fancybox gallery
  var fancyElements = [];
  // include the html elements in the fancybox gallery
  for (var i = 0, elLength = elements.length; i < elLength; i++) {
    fancyElements.push({
      href: elements[i].href,
      title: elements[i].title + " - " + elements[i].dataset.order,
      order: elements[i].dataset.order
    });
  }
  // sort the fancybox array of objects by the "order" property
  fancyElements.sort(sortObjectsBy("order", false, function(a) {
    return a;
  }));
  // programmatically launch fancybox
  $.fancybox(fancyElements, {
    helpers: {
      title: {
        type: "inside"
      }
    },
    // specify starting index
    index: thisIndex // commence gallery from the selected element
  })
  return false;
});

You have the flexibility to incorporate additional fancybox options into the script, similar to the inclusion of the title type. Moreover, the addition of the order to the title serves illustrative purposes to validate the orderly presentation determined by the data-order attribute.

Moreover, while the order property is non-essential for fancybox functionality, it facilitates sorting the array effectively.

Refer to JSFIDDLE for further details.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to implement a for loop within a Vue.js method

Within Vuejs, I have a method that updates multiple variables based on user selection. methods: { updateChart(){ this.chart1.series[1].data = [this.$store.state.selectedcities[0].value[1]]; this.chart1.series[2].data = [this.$store.state.selecte ...

How can I gather information from members who have already signed up?

I have a form that submits data to the Angular Firebase database. Once the form is submitted, I want to display the previously submitted data in the form if the user signs in again. Below is my .ts file: import { Component, OnInit } from '@angular/c ...

Using jQuery to Toggle the Height of Multiple Sections with a Single Function

I've got three different sections, each with varying heights and a simple structure like this: <section> <h2>My heading</h2> </section> What I want is for these sections to display at first, but then shrink dow ...

How to toggle the display of a div by its id using index in Javascript

Currently, I am encountering a problem with toggling the div containers. When I click on the video button, I want the other divs to close if they are already open. However, due to the toggling mechanism, if a div is closed and I click on the corresponding ...

What is the best way to ensure an image is shown in full screen exclusively on mobile devices?

My goal is to have two different site templates, one for desktop (which is already good) and one for mobile. For the mobile template, I want it to display only an image that fills the entire screen. The image should take up all the available space in term ...

conceal the bootstrap navigation bar while the page is being

Struggling to toggle a Bootstrap navbar on scroll? Need some guidance from the pros out there. I admit, my Bootstrap and jQuery skills are pretty basic. The browser console doesn't throw any errors, and I've experimented with fadeIn, fadeOut, add ...

How can I ensure the button remains disabled until all inputs are completed in Redux form?

I am currently studying a react-redux form example that you can find at the following link: In this example, the submit button is enabled if the user fills in at least one input field. I am curious to know if there is a way to enable the button only when ...

Event that signifies a change in the global state of the Angular 2 router

Is there a universal event that can be utilized for state change/start across all components, similar to the Component Lifecycle Hooks ? For example, in UI-router: $rootScope.$on("$stateChangeStart", function() {}) ...

Tips for triggering an event from a function instead of the window

Everything is functioning as expected, with the event listener successfully capturing the custom event when it is dispatched from the window and listened for as loading, all seems to be working well. const MyLib = mylib(); function mylib() { const re ...

What are the steps to fix the "Invariant Violation" issue that is linked to the redux store?

During my DOM testing to verify if a dialog box would open upon clicking a button, I encountered an error message: Invariant Violation: Could not find "store" in either the context or props of >"Connect(Photos)". Either wrap the root component in a , or ...

``How can I effectively handle and display Angular JSON text in an alert message?

Is there a way to pass a JSON entry into the onClick event for triggering an alert box in AngularJS? I'm attempting to display a message box with the content of a specific JSON entry when clicking on a row within a table. The issue seems to be isolat ...

Pass JS POST request data as body parameters

Is there a way to send a parameter as the post body to an API while also including the required Authorization header with the API key? How can I include a post body request with data set as "server_id: 12345"? What is the method to display the JSON res ...

Why is my HTML image stored in the database not showing up in TCPDF?

Currently, my situation involves using TCPDF to retrieve content from a MySQL table row that contains HTML code like this: <p><a href="x.html"><img src="http://1/2/3/x.jpg" width="x" height="x"></a></p> The problem arises wh ...

Puppeteer throwing an error when querying selectors cannot be done (TypeError: selector.startsWith is not a supported function)

I recently installed Puppeteer and ran into an issue when trying to query a selector. I keep receiving a TypeError: selector.startsWith is not a function error. I attempted to resolve the problem by tweaking the core code and adding toString(), but unfort ...

What is the best way to retrieve the value of the "Observer" object?

I am a user of Vue.js and I have the following data in this.suspendedReserveMemo: this.suspendedReserveMemo [__ob__: Observer]650: "memo3"651: "memo4"652: ""653: ""654: ""655: ""656: ""657: ""658: ""659: ""660:""661: ""662: ""length: 663__ob__: Observer {v ...

Exploring the proper syntax of the reduce() method in JavaScript

Here are two codes that can be executed from any browser. Code1: let prices = [1, 2, 3, 4, 5]; let result = prices.reduce( (x,y)=>{x+y} ); // Reduces data from x to y. console.log(result); Code2: let prices = [1, 2, 3, 4, 5]; let result = prices.red ...

What steps are involved in incorporating watchify into my gulp file?

I am looking to streamline the process of packing React using gulp in my nodeJS application. Below is the gulpfile I have set up: let gulp = require('gulp'); let uglify = require('gulp-uglify'); let browserify = require('browseri ...

The jQuery panel slider magically appears when you click the button, but refuses to disappear

One issue I am facing on my webpage involves a button that triggers a right panel to open using the jquery and modernizr frameworks. The button is positioned at the far right of the screen. When clicked, it slides to the left along with the panel it reveal ...

Display/conceal within a jQuery fixed navigation bar (center-aligned)

I am facing challenges with creating a sticky menu that shows/hides with a click button. Considering abandoning the show/hide feature altogether and rebuilding it from scratch in the future. Two major problems I have identified: How can I make the sho ...

What is the best way to assign a value to process.env within an npm script?

After creating a new Vue app (using Vite) with npm init vue@latest and selecting Playwright for e2e tests, the configuration file was generated with a field for setting headless mode: const config: PlaywrightTestConfig = { // ... use: { // ... ...