Inhaltsverzeichnis
keine Gliederung
(function () {
console.log('=== TRACKER START ===');
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');
if (!panel || !list) {
console.error('Panel or list not found!');
return;
}
var headings = [];
var currentIndex = -1;
var timer;
var scrollCount = 0;
function collectHeadings() {
var seenTexts = [];
var allHeadings = document.querySelectorAll(CONFIG.selectors);
var tempHeadings = [];
for (var i = 0; i < allHeadings.length; i++) {
var h = allHeadings[i];
var excluded = false;
for (var j = 0; j < CONFIG.excludeContainers.length; j++) {
var container = document.querySelector(CONFIG.excludeContainers[j]);
if (container && container.contains(h)) {
excluded = true;
break;
}
}
if (excluded) continue;
var text = h.textContent || h.innerText;
text = text.replace(/^\s+|\s+$/g, '');
var duplicate = false;
for (var m = 0; m < seenTexts.length; m++) {
if (seenTexts[m] === text) {
duplicate = true;
break;
}
}
if (!duplicate) {
seenTexts.push(text);
tempHeadings.push({ el: h, text: text });
}
}
headings = tempHeadings;
console.log('Found ' + headings.length + ' headings');
}
function getTop(el) {
var rect = el.getBoundingClientRect();
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
return rect.top + scrollTop;
}
function findCurrent() {
if (headings.length === 0) 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 === 0) 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.className = item.className + ' current';
} else if (Math.abs(i - currentIndex) === 1) {
item.className = item.className + ' near';
} else {
item.className = item.className + ' far';
}
item.appendChild(document.createTextNode(headings[i].text));
list.appendChild(item);
}
}
function update() {
if (headings.length === 0) return;
var idx = findCurrent();
console.log('Update: old=' + currentIndex + ', new=' + idx);
if (idx !== currentIndex) {
currentIndex = idx;
render();
}
}
function onScroll() {
scrollCount++;
console.log('SCROLL EVENT #' + scrollCount);
clearTimeout(timer);
timer = setTimeout(update, CONFIG.throttle);
}
console.log('Attaching scroll listener...');
if (window.addEventListener) {
window.addEventListener('scroll', onScroll, false);
window.addEventListener('resize', update, false);
console.log('addEventListener used');
} else if (window.attachEvent) {
window.attachEvent('onscroll', onScroll);
window.attachEvent('onresize', update);
console.log('attachEvent used (IE)');
}
function init(attempt) {
collectHeadings();
if (headings.length > 0) {
currentIndex = findCurrent();
console.log('Initial render at index: ' + currentIndex);
render();
return;
}
if (attempt < CONFIG.maxRetries) {
setTimeout(function() {
init(attempt + 1);
}, CONFIG.retryDelay);
}
}
setTimeout(function() {
init(0);
}, 1000);
}
if (document.readyState === 'loading') {
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', startTracker, false);
}
} else {
startTracker();
}
})();
<div id="trackerPageContent">
<div class="tracker-list" id="trackerList"></div>
</div>
#trackerPageContent {
position: fixed;
bottom: 110px;
left: 20px;
width: 250px;
z-index: 2147483000;
pointer-events: none;
opacity: 1;
}
.tracker-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.tracker-item {
line-height: 1.35;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tracker-item.current {
font-size: 22px;
font-weight: 600;
color: #0066CC;
}
.tracker-item.near {
font-size: 16px;
color: #777777;
}
.tracker-item.far {
font-size: 14px;
color: #7777776b;
}