dynamic-toc

Von $1

    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 :-)