export function initObserver(dotNetHelper, containerSelector, itemSelector) { const options = { root: null, rootMargin: '0px', threshold: 0.6 // 60% of the block must be visible }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const id = entry.target.id; const content = entry.target.innerText; dotNetHelper.invokeMethodAsync('HandleBlockReached', id, content); } }); }, options); const items = document.querySelectorAll(itemSelector); items.forEach(item => observer.observe(item)); return observer; } export function initScrollListener(dotNetHelper, scrollContainerSelector) { const container = document.querySelector(scrollContainerSelector); if (!container) return null; let isThrottled = false; const onScroll = () => { if (isThrottled) return; isThrottled = true; requestAnimationFrame(() => { const scrollTop = container.scrollTop; const scrollHeight = container.scrollHeight; const clientHeight = container.clientHeight; let percentage = 0; if (scrollHeight > clientHeight) { percentage = Math.round((scrollTop / (scrollHeight - clientHeight)) * 100); } // Ensure bounds percentage = Math.max(0, Math.min(100, percentage)); dotNetHelper.invokeMethodAsync('HandleScrollPercentChanged', percentage); isThrottled = false; }); }; container.addEventListener('scroll', onScroll, { passive: true }); // Initial calculation after a brief layout delay setTimeout(onScroll, 100); return { dispose: () => { container.removeEventListener('scroll', onScroll); } }; }