NuxtN
Nuxtβ€’14mo ago
yurian

Scrollspy is making goofy jumps

Hello there. I'm working on a scrollspy feature for my website. To change the hash, I'm using a simple
navigateTo
call, but I've tried with
router.push({hash: newHash})
and I get the same results, which is these goofy jumps that are shown in the video. Is there a way to make those navigations without the scroll going to to the top of the element the hash refers to?

This is my code:

<script lang="ts" setup>
const scrollspyContainer = ref<HTMLElement | null> (null)
const targets = ref<HTMLElement[]>([])

function isHTMLElement(element: Element): element is HTMLElement {
  return 'addEventListener' in element
}

function docOffsetTop(el: HTMLElement): number {
  const elRect = el.getBoundingClientRect()
  return elRect.top + window.scrollY
}

function process(container: Element): HTMLElement[] {
  const processImpl = (c: Element, acc: HTMLElement[] = []): HTMLElement[] => {
    if (isHTMLElement(c) && c.id !== '') {
      return acc.concat([c], Array.from(c.children).flatMap(child => processImpl(child, acc)))
    } else {
      return acc
    }
  }

  return processImpl(container)
    .sort((a, b) => docOffsetTop(a) - docOffsetTop(b)) // Sorts it in order of absolute height from the top of the document.
    .filter(c => c !== container)
    .map((c) => { c.classList.add('border', 'border-red-300'); return c })
}

onMounted(() => {
  if (scrollspyContainer.value) {
    targets.value = process(scrollspyContainer.value)
  }
})

const { y } = useWindowScroll()

watchThrottled(
  y,
  () => {
    const first = R.pipe(
      targets.value,
      R.dropWhile((el) => {
        const elComputedStyles = window.getComputedStyle(el)
        const marginTop = Number.parseFloat(elComputedStyles.scrollMarginTop) || 0
        const rect = el.getBoundingClientRect()

        return rect.bottom < marginTop
      }),
      R.first(),
    )

    if (first) {
      const firstHash = `#${first.id}`
      if (firstHash !== hash.value)
        navigateTo({ hash: firstHash })
    } else {
      navigateTo({ hash: '' })
    }
  },
  { throttle: 8.33 /* This is 120 frames per second. */ },
)
</script>


Also, when I collect the elements to be hashed on the route, it seems is only collecting those at the current component and not their children. Is there a workaround this?

Thanks a lot for reading this. Any help is really appreciated.
Was this page helpful?