Highlight Text While Scrolling

Hey there! If you have a Google Workspace account and are trying to use Gemini but keep getting an error, this guide is for you.

Bikers rights GIF via Giphy
"Bikers rights" Portlandia GIF via Giphy

Here’s the code from the video.

document.addEventListener('DOMContentLoaded', () => {
    // Configuration object with excluded terms
    const config = {
        excludedTerms: [
            'Key takeaways',
            // Add more terms here as needed
            // Example:
            // 'Summary',
            // 'Table of Contents',
            // etc.
        ]
    };

    // Rest of the styles remain the same
    const style = document.createElement('style');
    style.textContent = `
        @keyframes highlightMove {
            0% {
                background-size: 0% 100%;
                background-position: 0 0;
            }
            20% {
                background-size: 35% 100%;
                background-position: 0 0;
            }
            60% {
                background-size: 65% 100%;
                background-position: 0 0;
            }
            100% {
                background-size: 100% 100%;
                background-position: 0 0;
            }
        }

        .highlight-line {
            display: inline;
            padding: 3px 0;
            background-image: 
                repeating-linear-gradient(
                    -45deg,
                    rgba(255, 255, 0, 0) 0,
                    rgba(255, 255, 0, 0) 1px,
                    rgba(255, 255, 0, 0.5) 1px,
                    rgba(255, 255, 0, 0.5) 2px
                ),
                linear-gradient(
                    90deg,
                    rgba(255, 255, 0, 0.5) 0%,
                    rgba(255, 255, 0, 0.6) 15%,
                    rgba(255, 255, 0, 0.5) 85%,
                    rgba(255, 255, 0, 0.4) 100%
                );
            background-repeat: no-repeat;
            background-size: 0% 100%;
            background-position: 0 0;
            transition: none;
        }

        .highlight-line.active {
            animation: highlightMove 1.2s cubic-bezier(0.215, 0.610, 0.355, 1.000);
            animation-fill-mode: forwards;
        }

        .highlight-line::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: repeating-linear-gradient(
                -45deg,
                transparent 0,
                transparent 2px,
                rgba(255, 255, 0, 0.1) 2px,
                rgba(255, 255, 0, 0.1) 3px
            );
            mix-blend-mode: multiply;
            pointer-events: none;
        }
    `;
    document.head.appendChild(style);

    const processedPositions = new Map();
    const highlightedElements = new Set();

    function shouldHighlight(element) {
        const text = element.textContent.trim();
        return !config.excludedTerms.some(term => 
            text.toLowerCase() === term.toLowerCase()
        );
    }

    function getPositionSignature(element) {
        const rect = element.getBoundingClientRect();
        return `${rect.top}-${rect.left}-${element.textContent.trim()}`;
    }

    function wrapTextInHighlights(element) {
        // Skip if text is in excluded list
        if (!shouldHighlight(element)) {
            return;
        }

        const positionKey = getPositionSignature(element);
        
        if (processedPositions.has(positionKey)) {
            element.remove();
            return;
        }
        
        processedPositions.set(positionKey, true);
        
        const span = document.createElement('span');
        span.className = 'highlight-line';
        span.style.position = 'relative';
        
        while (element.firstChild) {
            span.appendChild(element.firstChild);
        }
        
        element.appendChild(span);
    }

    // Rest of the code remains the same...
    function isInViewport(element) {
        const rect = element.getBoundingClientRect();
        const buffer = window.innerHeight / 2;
        return (
            rect.top >= -buffer &&
            rect.top <= (window.innerHeight + buffer)
        );
    }

    let animationQueue = [];
    let isAnimating = false;

    async function animateHighlight(element) {
        if (highlightedElements.has(element)) return;
        
        element.classList.add('active');
        highlightedElements.add(element);
        await new Promise(resolve => setTimeout(resolve, 1200));
    }

    async function processQueue() {
        if (isAnimating || animationQueue.length === 0) return;
        
        isAnimating = true;
        
        while (animationQueue.length > 0) {
            const element = animationQueue.shift();
            if (!highlightedElements.has(element)) {
                await animateHighlight(element);
                await new Promise(resolve => setTimeout(resolve, 200));
            }
        }
        
        isAnimating = false;
    }

    const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting && !highlightedElements.has(entry.target)) {
                animationQueue.push(entry.target);
                processQueue();
            }
        });
    }, {
        root: null,
        rootMargin: '50px 0px',
        threshold: 0.1
    });

    setTimeout(() => {
        const boldElements = document.querySelectorAll('.entry-content p strong');
        boldElements.forEach(wrapTextInHighlights);

        document.querySelectorAll('.highlight-line').forEach(element => {
            observer.observe(element);
        });

        const initialElements = Array.from(document.querySelectorAll('.highlight-line'))
            .filter(element => isInViewport(element) && !highlightedElements.has(element));
        animationQueue.push(...initialElements);
        processQueue();
    }, 100);
});

Final thoughts

That concludes this article. What do you think? Let me know in the comments below (I read and reply to every comment). If you found this helpful, check out my full blog and subscribe to my YouTube channel. Thanks for reading!

Leave a Comment