Syncronize filter tabs between components

I have two components on the same page with identical sets of filter tabs. I’d like to synchronize the components so that clicking on a filter in one component selects the same/matching filter in the other component. I expect this might be possible with some custom javascript. Has anyone done something similiar?

1 Like

anyone? @Chem?

Here’s some code that can help.

You basically put in all the IDs of the components. Then when you if you click on the nth filter tab, it will click on the nth filter tab of the other components.

Demo can be seen here:

sync tabs

Code:

	var componentToSync = ['component_6', 'component_18']; // add a comma seperated list of component ids here. 

    jQuery('body').not('.not-assigned').on('click', '.filter-tabs > .nav.nav-tabs > li > a', function() {
        if (jQuery(this).hasClass('justClicked')) {
            jQuery(this).removeClass('justClicked');
            return;
        } else {
            var ele = [];
            for(var i in componentToSync) {
                ele.push(componentToSync[i].split('component_').join(''));
            }
            var eleData = '[data-obj-id="'+ele.join('"],[data-obj-id="')+'"]';
            if (jQuery(this).parents(eleData).length > 0) {
                var index = jQuery(this).parent().index();
                jQuery('[data-obj-id="'+ele.join('"] .filter-tabs > .nav.nav-tabs > li:eq('+index+') a,[data-obj-id="')+'"] .filter-tabs > .nav.nav-tabs > li:eq('+index+') a').not(this).addClass('justClicked').trigger('click');
            }
        }
    }).addClass('not-assigned');

I hope this helps, let me know if you have any other questions.

2 Likes

Moe, this is fantastic.
Much appreciated!

var componentList = ['component_1', 'component_2', 'component_3'];

var cmp = [];
componentList.forEach(x => {
   cmp.push('[data-obj-id="'+ x.replace('component_', '')+'"]'); 
});
cmp = cmp.join(',');
jQuery('body').on('click', '.filter-tabs > .nav.nav-tabs > li > a', function() {
    if (jQuery(this).hasClass('clicked')) {
        jQuery(this).removeClass('clicked');
        return;
    } else {
        let curPos = $(this).parent().index(),
            wrapper = jQuery(this).parents(cmp),
            components = $(cmp).not(wrapper);
        if(components.length){
            components.find(".filter-tabs li:eq("+ curPos +") a").addClass('clicked').trigger("click")
        }
    }
});
2 Likes

Hi Moe,

Thanks so much for sharing this — your original solution using index-based tab syncing across components was incredibly helpful and got me 90% of the way there :pray:

I did run into an edge case when working with calendar components, where the tabs appear to be rendered outside the component_X div or in a different DOM structure. This meant that matching by index wasn’t working reliably across different views (e.g. table vs. calendar).

To get around this, I ended up matching tabs by label text instead of index, using this:

$('body').not('.tabs-synced').on('click', 'a[ng-click*="setFilterTab"]', function () {
    const clickedText = $(this).text().trim();

    $('a[ng-click*="setFilterTab"]').each(function () {
        const tab = $(this);
        if (tab.text().trim() === clickedText && this !== event.target) {
            this.click();
        }
    });
}).addClass('tabs-synced');

This approach has been working well even when the DOM structure between components differs.

Thanks again for the foundational approach — it made a huge difference.
Hope this update helps someone else too!

Best,
Graham

1 Like