Inhaltsverzeichnis
keine Gliederung
(function () {
'use strict';
function startTracker() {
var CONFIG = {
selectors : 'h2, h3, h4',
threshold : 0.35,
throttle : 80,
range : 2,
excludeTexts : ['Von $1', '*Edit'],
excludeContainers : ['.deki-page-titlex', '#siteSubx'],
bottomDefault : 110,
bottomMenuOpen : 300,
maxRetries : 30,
retryDelay : 300
};
var panel = document.getElementById('trackerPageContent');
var list = document.getElementById('trackerList');
var menuToggle = document.querySelector('.menu-toggle-icon');
if (!panel || !list) return;
var headings = [];
var currentIndex = -1;
var timer;
var menuOpen = false;
if (menuToggle) {
menuToggle.addEventListener('click', function () {
menuOpen = !menuOpen;
panel.style.bottom = menuOpen ? CONFIG.bottomMenuOpen + 'px' : CONFIG.bottomDefault + 'px';
});
}
function collectHeadings() {
var seenTexts = [];
headings = Array.from(document.querySelectorAll(CONFIG.selectors))
.filter(function (h) {
for (var i = 0; i < CONFIG.excludeContainers.length; i++) {
if (h.closest(CONFIG.excludeContainers[i])) return false;
}
return true;
})
.map(function (h) {
return { el: h, text: h.textContent.trim() };
})
.filter(function (h) {
for (var i = 0; i < CONFIG.excludeTexts.length; i++) {
var pattern = CONFIG.excludeTexts[i];
if (pattern.startsWith('*') && h.text.endsWith(pattern.substring(1))) return false;
if (pattern.endsWith('*') && h.text.startsWith(pattern.slice(0, -1))) return false;
if (h.text === pattern) return false;
}
if (seenTexts.includes(h.text)) return false;
seenTexts.push(h.text);
return true;
});
}
function getTop(el) {
var rect = el.getBoundingClientRect();
return rect.top + (window.pageYOffset || document.documentElement.scrollTop);
}
function findCurrent() {
if (!headings.length) return 0;
var scrollPos = window.pageYOffset || document.documentElement.scrollTop;
var triggerPoint = scrollPos + (window.innerHeight * CONFIG.threshold);
for (var i = headings.length - 1; i >= 0; i--) {
var top = getTop(headings[i].el);
if (triggerPoint >= top) {
return i;
}
}
return 0;
}
function render() {
list.innerHTML = '';
if (!headings.length) return;
var start = Math.max(0, currentIndex - CONFIG.range);
var end = Math.min(headings.length - 1, currentIndex + CONFIG.range);
for (var i = start; i <= end; i++) {
var item = document.createElement('div');
item.className = 'tracker-item';
if (i === currentIndex) item.classList.add('current');
else if (Math.abs(i - currentIndex) === 1) item.classList.add('near');
else item.classList.add('far');
item.textContent = headings[i].text;
list.appendChild(item);
}
}
function update() {
if (!headings.length) return;
var idx = findCurrent();
if (idx !== currentIndex) {
currentIndex = idx;
render();
}
}
window.addEventListener('scroll', function () {
clearTimeout(timer);
timer = setTimeout(update, CONFIG.throttle);
}, { passive: true });
window.addEventListener('resize', update);
function init(attempt) {
collectHeadings();
if (headings.length > 0) {
currentIndex = findCurrent();
render();
return;
}
if (attempt < CONFIG.maxRetries) {
setTimeout(function() {
init(attempt + 1);
}, CONFIG.retryDelay);
}
}
setTimeout(function() {
init(0);
}, 1000);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startTracker);
} else {
startTracker();
}
})();
<div id="trackerPageContent">
<div class="tracker-list" id="trackerList"></div>
</div>
:root {
--tracker-width: 250px;
--tracker-bottom: 110px;
--tracker-left: 20px;
--size-current: 22px;
--size-near: 16px;
--size-far: 14px;
--color-current: #0066CC;
--color-near: #777777;
--color-far: #7777776b;
}
#trackerPageContent {
position: fixed;
bottom: 110px;
left: var(--tracker-left);
width: var(--tracker-width);
z-index: 2147483000;
pointer-events: none;
opacity: 1;
transition: bottom 0.3s ease, opacity 0.4s ease;
}
.tracker-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.tracker-item {
line-height: 1.35;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: all 0.25s ease;
}
.tracker-item.current {
font-size: var(--size-current);
font-weight: 600;
color: var(--color-current);
}
.tracker-item.near {
font-size: var(--size-near);
color: var(--color-near);
}
.tracker-item.far {
font-size: var(--size-far);
color: var(--color-far);
}