URL скопирован в буфер обмена!

Как сделать меню навигации по заголовкам H2 в потоках на Tilda

С помощью этой модификации можно создать меню с автоматической навигацией внутри стандартных потоков Tilda. Скрипт работает на базе блока меню ME604. Мод автоматически находит заголовки H2 в статье и добавляет их в меню, формируя удобное содержание для быстрого перехода по материалу.

Модификация работает с включенным Autoscale в блоках
Модификация работает только cо стандартными блоками
Номер модификации в библиотеке TiCode

Генератор кода

id блока меню
Смещение на пк
Смещение на мобилке
Кастомный первый пункт меню
Меню на мобилках
Войдите в аккаунт чтобы получить доступ к генератору кода
Этот генератор кода доступен только тем кто оплатил подписку
<!--TICODE--><!-- Библиотека модификаций для Тильда https://ticode.dev --> <!--TCD141--> <style> @media screen and (max-width: 960px) { .t607 { display: none !important; } } </style> <script> (function(){ const TCDmenuid = '#rec1976163493'; const TCDfirstMenu = false; const ticdrnd = () => 'tcdh2_' + Math.random().toString(36).slice(2,10); const tcdnorm = s => (s || '').replace(/\s+/g,' ').trim(); const ticlmp = (n,a,b) => Math.max(a, Math.min(b, n)); const tioff = (() => { const tiwidth = window.innerWidth || 0; const tcdmob = tiwidth <= 620; return tcdmob ? 80 : 100; })(); const ticodeFindAnchor = h2 => { if (h2.id) return h2.id; let tcprev = h2.previousElementSibling; while (tcprev && tcprev.nodeType === 1){ if (tcprev.matches && tcprev.matches('.t-redactor__anchor')){ if (!tcprev.id) tcprev.id = ticdrnd(); return tcprev.id; } if (tcprev.matches && (tcprev.matches('h1,h2,h3') || tcprev.classList.contains('t-redactor__text') || tcprev.tagName === 'BLOCKQUOTE')) break; tcprev = tcprev.previousElementSibling; } const tcanc = document.createElement('div'); tcanc.className = 't-redactor__anchor'; tcanc.id = ticdrnd(); tcanc.style.position = 'relative'; tcanc.style.top = '-10px'; h2.parentNode.insertBefore(tcanc, h2); return tcanc.id; }; const ticodeMaxScroll = () => { const tise = document.scrollingElement || document.documentElement; return Math.max(0, (tise.scrollHeight || 0) - window.innerHeight); }; const ticodeScrollTo = id => { const tiel = document.getElementById(id); if (!tiel) return; const tcdmax = ticodeMaxScroll(); let tiy = tiel.getBoundingClientRect().top + window.pageYOffset - tioff; tiy = ticlmp(tiy, 0, tcdmax); window.scrollTo({ top: tiy, behavior: 'smooth' }); }; const ticodePickActive = tcanch => { const tcdmax = ticodeMaxScroll(); if (tcdmax > 0 && (tcdmax - window.pageYOffset) <= 6) { return tcanch.length ? tcanch.length - 1 : -1; } const tiline = (window.innerHeight || 1) * 0.30; let tibest = -1, tidist = Infinity; for (let ti=0; ti<tcanch.length; ti++){ const tcel = document.getElementById(tcanch[ti]); if (!tcel) continue; const titop = tcel.getBoundingClientRect().top; const tcdst = Math.abs(titop - tiline); if (titop <= tiline + 30 && tcdst < tidist){ tidist = tcdst; tibest = ti; } } if (tibest === -1 && tcanch.length){ const tcfirst = document.getElementById(tcanch[0]); if (tcfirst && tcfirst.getBoundingClientRect().top > tiline) tibest = 0; } return tibest; }; const ticodeSetActive = (tclist, tcidx) => { const tilis = Array.from(tclist.querySelectorAll('.t607__list_item')); tilis.forEach(tili => { tili.classList.remove('is-active'); const tia = tili.querySelector('a.t-menu__link-item') || tili.querySelector('a'); if (tia) tia.classList.remove('t-active'); }); if (tcidx < 0) return; const tcli = tilis[tcidx]; if (!tcli) return; tcli.classList.add('is-active'); const tca = tcli.querySelector('a.t-menu__link-item') || tcli.querySelector('a'); if (tca) tca.classList.add('t-active'); }; const ticodeInit = () => { const tiroot = document.querySelector(TCDmenuid); if (!tiroot) return false; const tilist = tiroot.querySelector('.t607__list'); if (!tilist) return false; const ticont = document.querySelector('.t-feed__post-popup.t-popup_show .js-feed-post-text, .t-feed__post-popup .js-feed-post-text'); if (!ticont) return false; const tih2s = Array.from(ticont.querySelectorAll('h2.t-redactor__h2')) .map(tih => ({ el:tih, title:tcdnorm(tih.textContent) })) .filter(tix => tix.title.length > 0); const allItems = Array.from(tilist.querySelectorAll('.t607__list_item')); const tctpl = allItems[0]; if (!tctpl) return false; allItems.forEach((li, idx) => { if (TCDfirstMenu && idx === 0) return; li.remove(); }); if (!tih2s.length) return true; const tcanch = []; const ticodeCloneLi = (tcaid, tctit, tcidx) => { const tcli = tctpl.cloneNode(true); tcli.classList.remove('is-active'); const tca = tcli.querySelector('a.t-menu__link-item') || tcli.querySelector('a'); if (tca){ tca.classList.remove('t-active'); tca.setAttribute('href', '#' + tcaid); } const tctip = tcli.querySelector('.t607__tooltip'); if (tctip) tctip.textContent = tctit; tcli.addEventListener('click', function(tce){ tce.preventDefault(); tce.stopPropagation(); const liIndex = TCDfirstMenu ? (tcidx + 1) : tcidx; ticodeSetActive(tilist, liIndex); ticodeScrollTo(tcaid); try { history.replaceState(null, '', '#' + tcaid); } catch(_){} }, true); return tcli; }; tih2s.forEach((tix, tii) => { const tcaid = ticodeFindAnchor(tix.el); tcanch.push(tcaid); tilist.appendChild(ticodeCloneLi(tcaid, tix.title, tii)); }); let tiraf = 0; const ticodeScroll = () => { if (tiraf) return; tiraf = requestAnimationFrame(() => { tiraf = 0; const tcidx = ticodePickActive(tcanch); const liIndex = TCDfirstMenu ? (tcidx >= 0 ? tcidx + 1 : -1) : tcidx; ticodeSetActive(tilist, liIndex); }); }; window.removeEventListener('scroll', window.tcdscrollh, {passive:true}); window.tcdscrollh = ticodeScroll; window.addEventListener('scroll', ticodeScroll, {passive:true}); window.addEventListener('resize', ticodeScroll, {passive:true}); ticodeScroll(); const tipop = ticont.closest('.t-feed__post-popup'); if (tipop){ window.tcdkey = (tipop.getAttribute('data-feed-popup-postuid') || '') + '|' + (tipop.getAttribute('data-feed-popup-feeduid') || ''); } return true; }; let titries = 0; const titimer = setInterval(() => { titries++; const tiok = ticodeInit(); if (tiok || titries >= 10){ clearInterval(titimer); const tifeed = document.querySelector('.t-feed'); if (!tifeed) return; const timo = new MutationObserver(() => { const tipop = document.querySelector('.t-feed__post-popup.t-popup_show'); const tikey = tipop ? (tipop.getAttribute('data-feed-popup-postuid') || '') + '|' + (tipop.getAttribute('data-feed-popup-feeduid') || '') : ''; if (tikey && tikey !== window.tcdkey){ window.tcdkey = tikey; ticodeInit(); } else { ticodeInit(); } }); timo.observe(tifeed, { subtree:true, childList:true, attributes:true, attributeFilter:['class','data-feed-popup-postuid','data-feed-popup-feeduid'] }); window.tcdmo = timo; } }, 200); })(); </script> <script> (function TCDupdType() { const TildahtmlBclok = document.currentScript; if (TildahtmlBclok) { const recordElement = TildahtmlBclok.closest('.r[data-record-type]'); if (recordElement) { recordElement.setAttribute('data-record-type', Math.random().toString(36).substring(2, 12)); }}})(); </script>
КОПИРОВАТЬ КОД
1. Создаем пустую страницу и назначаем ее шапкой или футером в настройках потоков. Дополнительно в настройках потоков включаем опцию «открывать посты в виде страницы».

2. Создаем на этой странице блок ME604, копируем id вставляем в генератор в поле "id блока"

3. Указываем дополнительные настройки в генераторе:
Смещение на пк и Смещение на мобилках — задаем дополнительный отступ при прокрутке в пикселях, чтобы меню не перекрывало заголовок;
Кастомный первый пункт меню — если опция включена, можно использовать первый пункт меню вручную, например добавить ссылку назад к списку постов;
Меню на мобилках — включает отображение блока ME604 на экранах ниже 960 пикселей.

5. Копируем готовый код из генератора и вставляем в HTML блок T123. Блок кода размещаем под блоком меню.

6. В редакторе поста добавляем блоки H2. Именно они будут автоматически подтягиваться в меню. При необходимости заголовкам можно дополнительно назначать якорные ссылки, чтобы вывести содержание и внутри самого поста

Примечание: модификация работает только в постах, созданных в редакторе потоков Tilda. Также посты должны открываться в формате страницы.
Made on
Tilda