cferdinandi / tabby

Lightweight, accessible vanilla JS toggle tabs.
MIT License
598 stars 73 forks source link

Instantiate multiple instances with the same selector #108

Closed chopperoon closed 5 years ago

chopperoon commented 5 years ago

I'm trying to run multiple instances of Tabby on a page and can get this to work if I give each instance a unique selector and instantiate them independently:

<script>
var tabs = new Tabby('[data-tabs-1]');
var tabs = new Tabby('[data-tabs-2]');
</script>

Is there instead a way to instantiate all instances on the page which share a common selector?

Many thanks in advance.

danielpost commented 5 years ago

This doesn't seem to be possible at the moment, since Tabby runs querySelector on the selector, which by default only returns the first instance of the elements matching the selector.

You could probably solve this by running your own querySelectorAll to find all instances of your tabs, then initiate all of them separately. Try something like this (untested):

const tabSelectors = document.querySelectorAll('[data-tabs]');

for (const [i, tabs] of [...tabSelectors].entries()) {
    tabs.setAttribute(`data-tabs-${i}`, '');
    new Tabby(`[data-tabs-${i}]`);
}
chopperoon commented 5 years ago

That makes sense and your suggestion works perfectly. Many thanks for looking at this.

cferdinandi commented 5 years ago

The isolation is done on purpose because of how events are attached in Tabby. It would not work properly otherwise.

Closing this out since a solution was provided that seems to be working.

arnojong commented 4 years ago

The instantiating works great now, still having trouble with getting the toggle functionality to work. The documentation shows us you can do the following:

var tabs = new Tabby('data-tabs');
tabs.toggle('#harry');

However with this new way of instantiating multiple instances, the tabs are not defined with variables anymore.

new Tabby(`[data-tabs-${i}]`);

Is there a way to get the seperate tabs as variables so you can call tabs.toggle on them?

danielpost commented 4 years ago

@arnojong I think this should work (untested):

const tabsSelectors = document.querySelectorAll('[data-tabs]');

for (const [i, tabsContainer] of [...tabsSelectors].entries()) {
    tabsContainer.setAttribute(`data-tabs-${i}`, '');
    const tabs = new Tabby(`[data-tabs-${i}]`);

    tabs.toggle('#harry');
}

Please note that all code needs to be within the for..of loop

jacobleech commented 4 years ago

This doesn't seem to be possible at the moment, since Tabby runs querySelector on the selector, which by default only returns the first instance of the elements matching the selector.

You could probably solve this by running your own querySelectorAll to find all instances of your tabs, then initiate all of them separately. Try something like this (untested):

const tabSelectors = document.querySelectorAll('[data-tabs]');

for (const [i, tabs] of [...tabSelectors].entries()) {
    tabs.setAttribute(`data-tabs-${i}`, '');
    new Tabby(`[data-tabs-${i}]`);
}

Thanks for sharing this @danielpost

I wonder if it might be better doing something like:

const tabSelectors = document.querySelectorAll('[data-tabs]');

if (tabSelectors) {
 for (const [i, tabs] of [...tabSelectors].entries()) {
    tabs.setAttribute('data-tabs', i);
    new Tabby(`[data-tabs="${i}"]`);
  }
}

Adds an if statement to check that tabs exist and also saves adding another data attribute? Still seems to work well. Semantics I know.

jdimeo commented 4 years ago

Yes, please address this in the README or add support for this. thanks!

jdimeo commented 4 years ago

This worked for me, though it has a O(n²) problem since it toggles all matching tabs regardless of if it's currently on the right "parent" tab container:

const tabSelectors = document.querySelectorAll('[data-tabs]');
if (tabSelectors) {
 for (const [i, tabs] of [...tabSelectors].entries()) {
    tabs.setAttribute('data-tabs', i);
    new Tabby(`[data-tabs="${i}"]`);
  }
}

function toggleTabs(sel) {
  for ([i, tabs] of [...tabSelectors].entries()) {
    tabs = new Tabby(`[data-tabs="${i}"]`);
    document.querySelectorAll(sel).forEach(tab => tabs.toggle(tab));
  }
}