NeuroAgent

How to Destroy Fancybox in Bitrix After AJAX Loading

Solution for TypeError when destroying Fancybox instance after AJAX in Bitrix. Learn the correct destroy() methods for different Fancybox versions and integration with Bitrix AJAX.

Question

How to properly destroy a Fancybox instance after AJAX loading in Bitrix?

I’m trying to restart Fancybox after AJAX loading in the Bitrix catalog.element component. The code for the glide.js slider works correctly:

javascript
let fancyboxInstance = null;

document.addEventListener('DOMContentLoaded', function() {
    createOrUpdateFancybox();
});

BX.addCustomEvent("onAjaxSuccess", function() {
    createOrUpdateFancybox();
});

function createOrUpdateFancybox() {
    const slElements = document.querySelectorAll('[data-fancybox]');
    const slExist = slElements.length > 0;

    if (!slExist) return; 

    if (fancyboxInstance) {
        fancyboxInstance.destroy(); 
    }

    fancyboxInstance = Fancybox.bind("[data-fancybox]");
}

However, Bitrix outputs ‘processing’ to the console and gives an error: TypeError: fancyboxInstance.destroy is not a function. The close() method also doesn’t work. How do I properly destroy the previous Fancybox instance before creating a new one after AJAX loading?

NeuroAgent

Properly Destroying Fancybox Instances After AJAX Loading in Bitrix

To properly destroy a Fancybox instance after AJAX loading in Bitrix, you need to use the static method destroy() instead of calling it on the instance. Your problem is that the destroy() method is a static method of the Fancybox object, not an instance method.

Table of Contents

Main Problem and Solution

The error TypeError: fancyboxInstance.destroy is not a function occurs because you’re trying to call the destroy() method on a Fancybox instance when it’s actually a static method. According to Fancybox documentation, the correct call is:

javascript
// Close all instances and unbind all events
$.fancybox.destroy();

or for modern versions:

javascript
Fancybox.destroy();

Correct Code for Bitrix

Here’s the fixed version of your code:

javascript
let fancyboxInstance = null;

document.addEventListener('DOMContentLoaded', function() {
    createOrUpdateFancybox();
});

BX.addCustomEvent("onAjaxSuccess", function() {
    createOrUpdateFancybox();
});

function createOrUpdateFancybox() {
    const slElements = document.querySelectorAll('[data-fancybox]');
    const slExist = slElements.length > 0;

    if (!slExist) return; 

    // Destroy all existing Fancybox instances
    if (typeof Fancybox !== 'undefined') {
        Fancybox.destroy();
    }
    
    // Or for older versions:
    // if (typeof $.fancybox !== 'undefined') {
    //     $.fancybox.destroy();
    // }

    // Create a new instance
    fancyboxInstance = Fancybox.bind("[data-fancybox]");
}

Differences Between Fancybox Versions

It’s important to note that different Fancybox versions have different APIs:

  • Fancybox 2: Doesn’t include destroy or update methods according to StackOverflow
  • Fancybox 3+: Includes the static destroy() method as shown in the documentation
  • Fancybox 4: Modern version with full API
javascript
// For Fancybox 3/4
Fancybox.destroy();

// For older versions (alternative)
$(document).unbind('click.fb-start');
$('[data-fancybox]').unbind('click.fb');

Full Implementation with Error Handling

javascript
let fancyboxInstance = null;

document.addEventListener('DOMContentLoaded', function() {
    initFancybox();
});

BX.addCustomEvent("onAjaxSuccess", function() {
    initFancybox();
});

function initFancybox() {
    try {
        const fancyboxElements = document.querySelectorAll('[data-fancybox]');
        
        if (fancyboxElements.length === 0) {
            return;
        }

        // Destroy previous instances
        destroyFancybox();
        
        // Initialize a new instance
        fancyboxInstance = Fancybox.bind("[data-fancybox]", {
            // your options
        });
        
    } catch (error) {
        console.error('Fancybox initialization error:', error);
    }
}

function destroyFancybox() {
    try {
        // Check if Fancybox is available
        if (typeof Fancybox !== 'undefined') {
            Fancybox.destroy();
        } else if (typeof $.fancybox !== 'undefined') {
            // For older versions
            $.fancybox.close();
            $(document).unbind('click.fb-start');
            $('[data-fancybox]').unbind('click.fb');
        }
        
        // Reset the instance variable
        fancyboxInstance = null;
        
    } catch (error) {
        console.error('Fancybox destruction error:', error);
    }
}

Alternative Destruction Methods

If the destroy() method isn’t available in your Fancybox version, you can use alternative approaches:

1. Event Unbinding

javascript
function destroyFancyboxLegacy() {
    // Unbind all Fancybox events
    $(document).unbind('click.fb-start');
    $(document).unbind('click.fb');
    $(document).unbind('click.fb-close');
    
    // Unbind events from elements
    $('[data-fancybox]').unbind('click.fb');
    
    // Close all open instances
    if (typeof $.fancybox !== 'undefined') {
        $.fancybox.close();
    }
}

2. Setting the live: false Option

When initializing Fancybox, add the live: false option:

javascript
fancyboxInstance = Fancybox.bind("[data-fancybox]", {
    live: false  // This prevents automatic binding to new elements
});

As noted on StackOverflow, “Unbinding the .fancybox links only works if the live parameter is set to false.”

Integration with Bitrix AJAX

When working with Bitrix AJAX, it’s important to consider event handling specifics:

javascript
document.addEventListener('DOMContentLoaded', function() {
    initFancybox();
});

// Handle successful AJAX requests
BX.addCustomEvent("onAjaxSuccess", function() {
    // Small delay to ensure DOM is updated
    setTimeout(initFancybox, 100);
});

// Handle AJAX errors
BX.addCustomEvent("onAjaxFailure", function() {
    destroyFancybox();
});

function initFancybox() {
    const fancyboxElements = document.querySelectorAll('[data-fancybox]');
    
    if (fancyboxElements.length === 0) {
        return;
    }

    // Check that content is actually loaded
    let hasValidContent = false;
    fancyboxElements.forEach(element => {
        if (element.getAttribute('href') && element.getAttribute('href').trim() !== '') {
            hasValidContent = true;
        }
    });

    if (!hasValidContent) {
        return;
    }

    destroyFancybox();
    
    try {
        fancyboxInstance = Fancybox.bind("[data-fancybox]", {
            // Options for Bitrix
            touch: false,
            // other options
        });
    } catch (error) {
        console.error('Error initializing Fancybox in Bitrix:', error);
    }
}

Conclusion

  1. Use the static method Fancybox.destroy() or $.fancybox.destroy() instead of calling it on an instance
  2. Check the Fancybox version and use the appropriate API
  3. Handle errors during initialization and destruction
  4. Use a delay when handling Bitrix AJAX events to ensure DOM updates
  5. Consider alternative methods for older Fancybox versions involving event unbinding

Proper implementation will prevent conflicts when reloading content via AJAX in Bitrix and ensure popup windows work correctly.

Sources

  1. Fancybox Documentation - Core methods
  2. StackOverflow - How to destroy Fancybox?
  3. StackOverflow - Does the Fancybox instance need a reinitialise method?
  4. GitHub Issue - Reset a fancybox instance
  5. 1C-Bitrix Community - Fancybox and ajax bitrix
  6. Fancybox API Methods