I use scrollIntoView
in a Table of Content feature in a blog. When the user clicks on an item from the table of content, they are scrolled to the selected section.
However, on the initial click of an item, the user isn’t scrolled at the exact start of the section but just a bit under it.
Here comes the fun part, once the user selects the same item in the Table of Contents, they are properly scrolled to the start of the section.
So basically, the user needs to select a section twice before being scrolled to the exact start of the section.
This has been bugging me for a couple of hours now.
Any ideas what could be doing this?
Just for context I am using Vue.js 3 and below is the function that simply takes an element ID and scrolls to it.
The whole content I am rendering comes from CMS in the form of HTML and I am using the v-html
binding to render it.
function handleTocClick(elementId) {
isDropdownOpen.value = false;
const element = document.getElementById(elementId);
element.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'start' });
}
I tried different scrollIntoView options like block: start
or block: end
.
I also tried removing my scroll-margin-top
as I thought it could be causing this discrepancy, but nope, it is something else.
I also played with Vue’s nextTick()
like:
function handleTocClick(elementId) {
isDropdownOpen.value = false;
nextTick(() => {
const element = document.getElementById(elementId);
element.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'start' });
});
}
Full example:
<template>
<h6 @click="toggleTocDropdown">Table of content</h6>
<ul
:class="[
'toc-list',
{
dropdown: isMobile,
open: isDropdownOpen && isMobile,
},
]"
>
<li v-for="(article, index) in articleHeadings">
<a @click="handleTocClick(article.id)">{{ article.h2 }}</a>
</li>
</ul>
<div class="article-text">
<section v-for="(section, index) in articleSections" :id="section.id">
<h2 v-if="section.h2">
{{ section.h2 }}
</h2>
<div v-html="section.content"></div>
</section>
</div>
</template>
<script setup>
function handleTocClick(elementId) {
const element = document.getElementById(elementId);
element.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'start',
});
isDropdownOpen.value = false;
}
</script>