• Табы на js, как правильно?

    Самое первое - отказаться от неоправданного изменения html. Все эти `data-index` - признак плохого кода. Любой блок должен, по возможности, получаться в JS только один раз, записываться в кеш и работать далее с блоком именно как с JS инстансом.

    Второе - разделить глобальное управление и каждый таб в отдельности. Каждый логический блок дожлен быть инстансом класса - тогда значительно легче с этим орудовать и дебажить.

    Третье - изменения html максимально выносим в отдельный метод.

    Ну и вообще - все действия разбить на блоки в виде методов.

    Как разультат:

    const TabItemSelector = '.pageNav__tabItem';
    const ContentItemSelector = '.pageNav__contentItem';
    
    class TabsManager {
      constructor(navNode){
        this.tabs = [];
        this.activeTab = null;
    
        this.initFromHtml(navNode);
        this.activateTab(this.tabs[0]);
      }
    
      initFromHtml (navNode) {
        const headers  = navNode.querySelectorAll(TabItemSelector);
        const contents = navNode.querySelectorAll(ContentItemSelector);
    
        for (var i = 0; i < headers.length; i++) {
            this.registerTab(headers[i], contents[i]);
        }
      }
    
      registerTab (header, content) {
        const tab = new TabItem(header, content);
        tab.onActivate(() => this.activateTab(tab));
        this.tabs.push(tab);
      }
      
      activateTab (tabItem) {
        if (this.activeTab) {
            this.activeTab.setActive(false);
        }
    
        this.activeTab = tabItem;
        this.activeTab.setActive(true);
      }
      
    }
    
    const ActiveTabHeaderClass = 'pageNav__tabItem--active';
    const ActiveTabContentClass = 'pageNav__contentItem--active';
    
    class TabItem {
        constructor (header, content) {
            this.header  = header;
            this.content = content;
        }
        onActivate (action) {
            this.header.addEventListener('click', () => action(this));
        }
        setActive(value) {
            this.header.classList.toggle(ActiveTabHeaderClass, value);
            this.content.classList.toggle(ActiveTabContentClass, value);
        }
    }
    
    document.addEventListener('DOMContentLoaded', ()=>{
      let tabs = new TabsManager(document.querySelector('.pageNav'));
    })


    Самое сомнительное в этом коде - это, конечно, TabsManager.initFromHtml, где создается взаимосвязь между headers и contents (к примеру, что будет если заголовков табов будет больше, чем детей?). Но это получается из-за верстки.
    Ответ написан