dynamic-toc

Von $1

  • Sie haben keine Berechtigung, die Beschränkungen auf dieser Seite zu ändern.
Inhaltsverzeichnis
keine Gliederung
// =============================================================================
// HEADING TRACKER - Production Version
// Zeigt aktuelle Position auf langen Seiten anhand von Überschriften
// 
// Features:
// - Robuste Höhen-Erkennung (funktioniert auch bei height: 100%)
// - Text-Filter (Von $1, *Edit)
// - Container-Ausschluss
// - Duplikat-Erkennung
// - Auto-Verstecken < 1000px von oben
// - Selbstprüfung (min 3 headings, min 1500px Seite)
// - Debug-Logging in Console
// =============================================================================

!function() {
    
    function startTracker() {
        console.log("%c=== HEADING TRACKER INIT ===", "color: #0066CC; font-weight: bold; font-size: 14px");
        
        var panel = document.getElementById("trackerPageContent");
        var list = document.getElementById("trackerList");
        
        if (!panel || !list) {
            console.error("❌ Tracker: Panel oder List Element nicht gefunden!");
            return;
        }
        
        console.log("✅ DOM Elemente gefunden");
        
        var headings = [];
        var currentIndex = -1;
        var timer;
        var isVisible = 1;
        var lastScrollPos = 0;
        var scrollEventCount = 0;
        
        var CONFIG = {
            selectors: "h2,h3,h4",
            threshold: 0.35,
            throttle: 80,
            range: 2,
            minH: 3,
            minPage: 1500,
            hideTop: 1000,
            excludeTexts: ["Von $1", "*Edit"],
            excludeContainers: [".deki-page-titlex", "#siteSubx"]
        };
        
        function getRealPageHeight() {
            var docEl = document.documentElement;
            var body = document.body;
            
            return Math.max(
                docEl.scrollHeight || 0,
                body.scrollHeight || 0,
                docEl.offsetHeight || 0,
                body.offsetHeight || 0,
                docEl.clientHeight || 0,
                body.clientHeight || 0
            );
        }
        
        function shouldActivate() {
            var pageHeight = getRealPageHeight();
            if (pageHeight < CONFIG.minPage) return 0;
            collectHeadings();
            return headings.length >= CONFIG.minH;
        }
        
        function collectHeadings() {
            var allHeadings = document.querySelectorAll(CONFIG.selectors);
            var seenTexts = [];
            var tempHeadings = [];
            
            console.log("�� Sammle Headings... (" + allHeadings.length + " gefunden)");
            
            for (var i = 0; i < allHeadings.length; i++) {
                var element = allHeadings[i];
                var skip = 0;
                
                for (var j = 0; j < CONFIG.excludeContainers.length; j++) {
                    var container = document.querySelector(CONFIG.excludeContainers[j]);
                    if (container && container.contains(element)) {
                        skip = 1;
                        break;
                    }
                }
                
                if (skip) continue;
                
                var text = (element.textContent || element.innerText).replace(/^\s+|\s+$/g, "");
                
                for (var k = 0; k < CONFIG.excludeTexts.length; k++) {
                    var pattern = CONFIG.excludeTexts[k];
                    
                    if (pattern.charAt(0) === "*") {
                        var suffix = pattern.substring(1);
                        if (text.indexOf(suffix, text.length - suffix.length) !== -1) {
                            skip = 1;
                            break;
                        }
                    }
                    else if (pattern.indexOf("$1") !== -1) {
                        var prefix = pattern.split("$1")[0];
                        if (text.indexOf(prefix) === 0) {
                            skip = 1;
                            break;
                        }
                    }
                    else if (text === pattern) {
                        skip = 1;
                        break;
                    }
                }
                
                if (skip) continue;
                
                var isDuplicate = 0;
                for (var m = 0; m < seenTexts.length; m++) {
                    if (seenTexts[m] === text) {
                        isDuplicate = 1;
                        break;
                    }
                }
                
                if (!isDuplicate) {
                    seenTexts.push(text);
                    tempHeadings.push({el: element, text: text});
                }
            }
            
            headings = tempHeadings;
            console.log("✅ " + headings.length + " Headings nach Filterung");
        }
        
        function getTop(element) {
            var rect = element.getBoundingClientRect();
            var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
            return rect.top + scrollTop;
        }
        
        function findCurrent() {
            if (!headings.length) return 0;
            
            var scrollPos = window.pageYOffset || document.documentElement.scrollTop;
            var viewportHeight = window.innerHeight;
            var pageHeight = getRealPageHeight();
            
            if (scrollPos + viewportHeight >= pageHeight - 100) {
                return headings.length - 1;
            }
            
            var triggerPoint = scrollPos + viewportHeight * CONFIG.threshold;
            
            for (var i = headings.length - 1; i >= 0; i--) {
                if (triggerPoint >= getTop(headings[i].el)) {
                    return i;
                }
            }
            
            return 0;
        }
        
        function render() {
            list.innerHTML = "";
            
            if (headings.length) {
                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 += " current";
                    } else if (Math.abs(i - currentIndex) === 1) {
                        item.className += " near";
                    } else {
                        item.className += " far";
                    }
                    
                    item.textContent = headings[i].text;
                    list.appendChild(item);
                }
            }
        }
        
        function checkVisibility() {
            var scrollPos = window.pageYOffset || document.documentElement.scrollTop;
            
            if (scrollPos < CONFIG.hideTop) {
                if (isVisible) {
                    panel.style.opacity = "0";
                    isVisible = 0;
                }
            } else {
                if (!isVisible) {
                    panel.style.opacity = "1";
                    isVisible = 1;
                    console.log("��️ Tracker sichtbar (Scroll > " + CONFIG.hideTop + "px)");
                }
            }
        }
        
        function update() {
            var scrollPos = window.pageYOffset || document.documentElement.scrollTop;
            
            if (Math.abs(scrollPos - lastScrollPos) < 10) return;
            lastScrollPos = scrollPos;
            
            checkVisibility();
            
            var newIndex = findCurrent();
            if (newIndex !== currentIndex) {
                currentIndex = newIndex;
                render();
                
                scrollEventCount++;
                if (scrollEventCount % 5 === 0) {
                    console.log("�� Position: " + (currentIndex + 1) + "/" + headings.length + " - " + headings[currentIndex].text.substring(0, 40));
                }
            }
        }
        
        function init() {
            var pageHeight = getRealPageHeight();
            
            console.log("%c--- Aktivierungs-Check ---", "color: #666; font-style: italic");
            console.log("�� Seitenhöhe:", pageHeight + "px (min: " + CONFIG.minPage + "px)");
            
            collectHeadings();
            
            console.log("�� Headings:", headings.length + " (min: " + CONFIG.minH + ")");
            
            if (!shouldActivate()) {
                panel.style.display = "none";
                console.log("%c❌ TRACKER DEAKTIVIERT", "color: #ff0000; font-weight: bold");
                console.log("Grund: " + (pageHeight < CONFIG.minPage ? "Seite zu kurz" : "Zu wenig Headings"));
                return;
            }
            
            console.log("%c✅ TRACKER AKTIVIERT!", "color: #00cc00; font-weight: bold; font-size: 14px");
            
            panel.style.opacity = "1";
            currentIndex = findCurrent();
            render();
            
            console.log("�� Start bei Heading: " + (currentIndex + 1) + "/" + headings.length);
            console.log("�� Scroll Listener aktiv");
            
            window.addEventListener("scroll", function() {
                clearTimeout(timer);
                timer = setTimeout(update, CONFIG.throttle);
            }, false);
            
            checkVisibility();
        }
        
        setTimeout(init, 1500);
    }
    
    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", startTracker);
    } 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: 0;
    transition: 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;
}

.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: #999999;
}
Tags: (Schlagworte bearbeiten)
  • No tags
 
Kommentare (0)
Um einen Kommentar zu schreiben, müssen Sie sich anmelden.

 
Use the language selection below for English Utilise le sélecteur de langue ci-dessous pour français Per l'italiano, utilizzare la selezione della lingua qui sotto Schön sprichst du deutsch :-)