dynamic-toc

Von $1

    Inhaltsverzeichnis
    keine Gliederung

    Version seit 15:43, 5 Nov 2026

    zu dieser Version.

    Zurück zu Versionshistorie.

    Zeige aktuelle Version

    // =============================================================================
    // HEADING TRACKER - Finale Version
    // Zeigt aktuelle Position auf langen Seiten anhand von Überschriften
    // =============================================================================
    
    !function() {
        
        function startTracker() {
            // DOM Elemente
            var panel = document.getElementById("trackerPageContent");
            var list = document.getElementById("trackerList");
            
            if (!panel || !list) return;
            
            // State
            var headings = [];
            var currentIndex = -1;
            var timer;
            var isVisible = 1;
            var lastScrollPos = 0;
            
            // =============================================================================
            // KONFIGURATION
            // =============================================================================
            var CONFIG = {
                selectors: "h2,h3,h4",              // Welche Headings tracken
                threshold: 0.35,                     // Trigger bei 35% Viewport
                throttle: 80,                        // Scroll delay in ms
                range: 2,                            // Anzahl Headings vor/nach aktuellem
                minH: 3,                             // Minimum Headings für Aktivierung
                minPage: 1500,                       // Minimum Seitenhöhe in px
                hideTop: 1000,                       // Verstecke wenn < 1000px von oben
                excludeTexts: ["Von $1", "*Edit"],  // Text-Filter (Prefix/Suffix)
                excludeContainers: [                 // Container die ignoriert werden
                    ".deki-page-titlex",
                    "#siteSubx"
                ]
            };
            
            // =============================================================================
            // PRÜFE OB TRACKER AKTIVIERT WERDEN SOLL
            // =============================================================================
            function shouldActivate() {
                var pageHeight = Math.max(
                    document.documentElement.scrollHeight || 0,
                    document.body.scrollHeight || 0
                );
                
                if (pageHeight < CONFIG.minPage) return 0;
                
                collectHeadings();
                
                return headings.length >= CONFIG.minH;
            }
            
            // =============================================================================
            // SAMMLE HEADINGS MIT FILTERN
            // =============================================================================
            function collectHeadings() {
                var allHeadings = document.querySelectorAll(CONFIG.selectors);
                var seenTexts = [];
                var tempHeadings = [];
                
                for (var i = 0; i < allHeadings.length; i++) {
                    var element = allHeadings[i];
                    var skip = 0;
                    
                    // Prüfe excludeContainers
                    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;
                    
                    // Text extrahieren und trimmen
                    var text = (element.textContent || element.innerText).replace(/^\s+|\s+$/g, "");
                    
                    // Prüfe excludeTexts
                    for (var k = 0; k < CONFIG.excludeTexts.length; k++) {
                        var pattern = CONFIG.excludeTexts[k];
                        
                        // Suffix-Match (*Edit)
                        if (pattern.charAt(0) === "*") {
                            var suffix = pattern.substring(1);
                            if (text.indexOf(suffix, text.length - suffix.length) !== -1) {
                                skip = 1;
                                break;
                            }
                        }
                        // Prefix-Match mit Wildcard (Von $1)
                        else if (pattern.indexOf("$1") !== -1) {
                            var prefix = pattern.split("$1")[0];
                            if (text.indexOf(prefix) === 0) {
                                skip = 1;
                                break;
                            }
                        }
                        // Exact Match
                        else if (text === pattern) {
                            skip = 1;
                            break;
                        }
                    }
                    
                    if (skip) continue;
                    
                    // Prüfe Duplikate
                    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;
            }
            
            // =============================================================================
            // BERECHNE ABSOLUTE POSITION EINES ELEMENTS
            // =============================================================================
            function getTop(element) {
                var rect = element.getBoundingClientRect();
                var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
                return rect.top + scrollTop;
            }
            
            // =============================================================================
            // FINDE AKTUELLES HEADING
            // =============================================================================
            function findCurrent() {
                if (!headings.length) return 0;
                
                var scrollPos = window.pageYOffset || document.documentElement.scrollTop;
                var viewportHeight = window.innerHeight;
                var pageHeight = Math.max(
                    document.documentElement.scrollHeight,
                    document.body.scrollHeight
                );
                
                // Am Ende der Seite?
                if (scrollPos + viewportHeight >= pageHeight - 100) {
                    return headings.length - 1;
                }
                
                // Finde Heading bei Trigger-Point
                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;
            }
            
            // =============================================================================
            // RENDERE TRACKER
            // =============================================================================
            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);
                    }
                }
            }
            
            // =============================================================================
            // VISIBILITY CHECK - VERSTECKE BEI < 1000PX VON OBEN
            // =============================================================================
            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;
                    }
                }
            }
            
            // =============================================================================
            // UPDATE BEI SCROLL
            // =============================================================================
            function update() {
                var scrollPos = window.pageYOffset || document.documentElement.scrollTop;
                
                // Throttle: Nur bei signifikanter Bewegung
                if (Math.abs(scrollPos - lastScrollPos) < 10) return;
                lastScrollPos = scrollPos;
                
                checkVisibility();
                
                var newIndex = findCurrent();
                if (newIndex !== currentIndex) {
                    currentIndex = newIndex;
                    render();
                }
            }
            
            // =============================================================================
            // INITIALISIERUNG
            // =============================================================================
            function init() {
                if (!shouldActivate()) {
                    panel.style.display = "none";
                    return;
                }
                
                panel.style.opacity = "1";
                currentIndex = findCurrent();
                render();
                
                window.addEventListener("scroll", function() {
                    clearTimeout(timer);
                    timer = setTimeout(update, CONFIG.throttle);
                }, false);
                
                checkVisibility();
            }
            
            setTimeout(init, 1500);
        }
        
        // =============================================================================
        // AUTO-START
        // =============================================================================
        if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", startTracker);
        } else {
            startTracker();
        }
        
    }();
    
    <div id="trackerPageContent">
     <div class="tracker-list" id="trackerList"></div>
    </div>
    /* =============================================================================
       TRACKER STYLING
       ============================================================================= */
    
    #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;
    }
    
    /* Aktuelles Heading - groß & blau */
    .tracker-item.current {
        font-size: 22px;
        font-weight: 600;
        color: #0066CC;
    }
    
    /* Nächste Headings - mittel */
    .tracker-item.near {
        font-size: 16px;
        color: #777777;
    }
    
    /* Entfernte Headings - klein */
    .tracker-item.far {
        font-size: 14px;
        color: #999999;
    }
    

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