демоверсия
Крутите колесо
Сейчас узнаем, какой подарок выпадет именно Вам
Как переместить табы в карточке товара справа под текст в Tilda

Как переместить табы в карточке товара справа под текст в Tilda

1
Создали товары в каталоге и вывели их в блок ST340
2
В нужных товарах заполнили табы
3
Добавили в обычный и продуктовый footer блок Т123
и вставили в него код
Mo-ti Level Up
Видео инструкции по добавлению кода и работе с Zero Block.
Как переместить табы в карточке товара справа под текст в Tilda
Фрагмент видео
Библиотека для примера
<script>
'use strict';

(function () {
  const ready = fn => document.readyState === 'loading'
    ? document.addEventListener('DOMContentLoaded', fn)
    : fn();

  ready(function () {

    const DEBUG = false;
    const log = (...a) => DEBUG && console.log('[TABS]', ...a);

    // true  — первая вкладка открыта
    // false — все вкладки закрыты
    const OPEN_FIRST_TAB_POPUP = false;   // карточка в popup
    const OPEN_FIRST_TAB_PAGE = false;   // отдельная страница товара
    const REVEAL_DELAY = 300;     
    
    const PREFIXES = ['t-store', 't-catalog'];
    const both = tpl => PREFIXES.map(p => tpl.replace(/\{p\}/g, p)).join(', ');

    const SEL = {
      CARD_POPUP: both('.{p} .t-popup_show .{p}__product-popup'),
      CARD_PAGE: both('.{p}__product-snippet'),
      INFO: both('.{p}__prod-popup__info') + ', ' + both('.{p}__prod-snippet__info'),
      TABS: both('.{p}__tabs')
    };

   
    function getPrefix(tabs) {
      return PREFIXES.find(p => tabs.classList.contains(p + '__tabs')) || PREFIXES[0];
    }

    function closeAllTabs(tabs, p) {
      tabs.querySelectorAll('.' + p + '__tabs__item_active')
        .forEach(el => el.classList.remove(p + '__tabs__item_active'));
      tabs.querySelectorAll('.' + p + '__tabs__item-button_active')
        .forEach(el => el.classList.remove(p + '__tabs__item-button_active'));
      tabs.querySelectorAll('.' + p + '__tabs__button_active')
        .forEach(el => el.classList.remove(p + '__tabs__button_active'));
      tabs.querySelectorAll('.' + p + '__tabs__content')
        .forEach(el => { el.style.display = 'none'; });
    }

    function openFirstTab(tabs, p) {
      closeAllTabs(tabs, p);
      const first = tabs.querySelector('.' + p + '__tabs__item');
      if (!first) return;
      first.classList.add(p + '__tabs__item_active');
      first.querySelector('.' + p + '__tabs__item-button')
        ?.classList.add(p + '__tabs__item-button_active');
      const content = first.querySelector('.' + p + '__tabs__content');
      if (content) content.style.display = 'block';

      tabs.querySelector('.' + p + '__tabs__button')
        ?.classList.add(p + '__tabs__button_active');
    }

    function applyState(tabs, open) {
      if (tabs.dataset.userTouched === '1') return;
      const p = getPrefix(tabs);
      if (open) openFirstTab(tabs, p);
      else closeAllTabs(tabs, p);
    }

    function isState(tabs, open) {
      const p = getPrefix(tabs);
      const items = Array.from(tabs.querySelectorAll('.' + p + '__tabs__item'));
      const active = items.filter(i => i.classList.contains(p + '__tabs__item_active'));
      const visible = Array.from(tabs.querySelectorAll('.' + p + '__tabs__content'))
        .filter(c => c.style.display === 'block');
      if (open) {
        return active.length === 1 && active[0] === items[0] && visible.length === 1;
      }
      return active.length === 0 && visible.length === 0;
    }

    function enforceState(tabs, open) {
      applyState(tabs, open);

      const obs = new MutationObserver(() => {
        if (!tabs.isConnected || tabs.dataset.userTouched === '1') {
          obs.disconnect();
          return;
        }
        if (isState(tabs, open)) return;
        applyState(tabs, open);
        obs.takeRecords(); 
        log('state restored');
      });
      obs.observe(tabs, {
        subtree: true,
        attributes: true,
        attributeFilter: ['class', 'style']
      });

      setTimeout(() => {
        tabs.style.setProperty('opacity', '1', 'important');
        tabs.classList.add('tab-element_ready');
      }, REVEAL_DELAY);
    }

    function processCard(card, isPopup) {
      const info = card.querySelector(SEL.INFO);
      const tabs = card.querySelector(SEL.TABS);
      if (!info || !tabs) return;                    
      if (tabs.dataset.tabsProcessed === '1') return;
      tabs.dataset.tabsProcessed = '1';

      tabs.classList.add('tab-element');
      if (!info.contains(tabs)) info.appendChild(tabs);
      log('tabs processed', card);

      enforceState(tabs, isPopup ? OPEN_FIRST_TAB_POPUP : OPEN_FIRST_TAB_PAGE);
    }

    function runInit() {
      document.querySelectorAll(SEL.CARD_PAGE).forEach(c => processCard(c, false));
      document.querySelectorAll(SEL.CARD_POPUP).forEach(c => processCard(c, true));
    }

    document.addEventListener('click', ev => {
      const tabs = ev.target.closest(SEL.TABS);
      if (tabs) tabs.dataset.userTouched = '1';
    }, true);

    
    let moTimer;
    const mo = new MutationObserver(() => {
      clearTimeout(moTimer);
      moTimer = setTimeout(runInit, 100);
    });

    mo.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['class']   
    });

    runInit();
  });
})();
</script>




<style>


.t-store__prod-popup__container,
.t-catalog__prod-popup__container
{
    padding-top: 0;
}



#allrecords .t-store__tabs.tab-element,
#allrecords .t-catalog__tabs.tab-element
{
    margin-left: 0;
    margin-right: 0;
    margin-top: 15px;
    max-width: none;
}



.t-store__prod-popup__info::-webkit-scrollbar-thumb:hover,
.t-catalog__prod-popup__info::-webkit-scrollbar-thumb:hover
{
    background: rgba(0, 0, 0, 0.2);
}

.t-store__prod-popup__info::-webkit-scrollbar-track,
.t-catalog__prod-popup__info::-webkit-scrollbar-track
{
    background: transparent;
}


@media screen and (min-width: 980px) {
    .t-store__prod-popup__col_fixed,
    .t-catalog__prod-popup__col_fixed 
    {
        top: 20px;
    }
}

@media screen and (max-width: 980px) {
    .t-store.t-store__prod-snippet__container .t-store__tabs,
    .t-catalog.t-catalog__prod-snippet__container .t-catalog__tabs
    
    {
        padding-left: 0px;
        padding-right: 0px;
    }
}


@media screen and (max-width: 1200px) {
    #allrecords .t-store__tabs__item-title,
    #allrecords .t-catalog__tabs__item-title
    
    {
        font-size: 15px;
    }
}



</style>
Made on
Tilda