Issue with Vue Router failing to navigate to the correct hash location

After clicking a link on the app, I noticed that the Router does scroll to the correct hash location, but it always seems to be one element too high on the page. This issue occurs even though the scrolling functionality is working fine. For instance, if my page structure is as follows:

<template>
    <div id="div1">Div1</div>
    <div id="div2">Div2</div>
    <div id="div3">Div3</div>
    <div id="div4">Div4</div>
</template>

When I click a link that should go to hash "div3," it actually scrolls me to the top of div2 instead. Only a few elements are scrolled to correctly. Could this be due to some setting related to page margins?

Here's the snippet of code for the Router:

const router = new VueRouter({
  routes,
  mode: 'history',
  base: "/",
  scrollBehavior (to, from, savedPosition) {
    if (to.hash) {
        return {
          selector: to.hash,
          behavior: 'smooth',
        }
    } else {
        return { x: 0, y: 0 }
    }
  }
})

Below is an example of how the hash routing is called in the code:

if (item.title == "Mission") {
    router.push({name: 'Explore', hash: '#mission-statement'});
} else if (item.title == "Our Story") {
    router.push({name: 'Explore', hash: '#our-story-container'});
} else if (item.title == "Shared principles") {
    router.push({name: 'Explore', hash: '#shared-principles-container'});
} else if (item.title == "Volunteer Opportunities") {
    router.push({name: 'Explore', hash: '#volunteer-container'});
} else if (item.title == "Gallery") {
    router.push({name: 'Explore', hash: '#galleries'});
} else if (item.title == "Living") {
    router.push({name: 'Explore', hash: '#living-container'});
} else if (item.title == "Contact Us") {
    router.push({name: 'Explore', hash: '#contact-us-container'});
} else {
    router.push("/explore")
}

SOLUTION:

Thanks to IVO GELOV's answer, I was able to update the scroll behavior function with the following code, and now everything works as expected.

scrollBehavior (to, from, savedPosition) {
    let position = {}
    if (to.hash) {
        position = {
          selector: to.hash,
          offset: { x: 0, y: 100 },
          behavior: 'smooth',
        }
    } else {
        position = { x: 0, y: 0 }
    }
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(position)
      }, 100)
    })
  }

Answer №1

Nuxt.js incorporates the code snippet below in its ./nuxt/router.js. You could potentially adapt this code to suit your specific needs:

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router) 

if (process.client) {
  window.history.scrollRestoration = 'manual'
}
const scrollBehavior = function (to, from, savedPosition) {
  // if the returned position is falsy or an empty object,
  // will retain current scroll position.
  let position = false

  // if no children detected
  if (to.matched.length < 2) {
    // scroll to the top of the page
    position = { x: 0, y: 0 }
  } else if (to.matched.some((r) => r.components.default.options.scrollToTop)) {
    // if one of the children has scrollToTop option set to true
    position = { x: 0, y: 0 }
  }

  // savedPosition is only available for popstate navigations (back button)
  if (savedPosition) {
    position = savedPosition
  }

  return new Promise(resolve => {
    // wait for the out transition to complete (if necessary)
    window.$nuxt.$once('triggerScroll', () => {
      // coords will be used if no selector is provided,
      // or if the selector didn't match any element.
      if (to.hash) {
        let hash = to.hash
        // CSS.escape() is not supported with IE and Edge.
        if (typeof window.CSS !== 'undefined' && typeof window.CSS.escape !== 'undefined') {
          hash = '#' + window.CSS.escape(hash.substr(1))
        }
        try {
          if (document.querySelector(hash)) {
            // scroll to anchor by returning the selector
            position = { selector: hash }
          }
        } catch (e) {
          console.warn('Failed to save scroll position. Please add CSS.escape() polyfill (https://github.com/mathiasbynens/CSS.escape).')
        }
      }
      resolve(position)
    })
  })
}

export function createRouter () {
  return new Router({
    mode: 'history',
    base: '/',
    linkActiveClass: 'nuxt-link-active',
    linkExactActiveClass: 'nuxt-link-exact-active',
    scrollBehavior,
    routes: [ .... ],
    fallback: false
  })
}

A practical demonstration (sourced from https://dev.to/dimer191996/nuxt-js-smooth-scrolling-with-hash-links-94a) - you might also find it necessary to specify

window.history.scrollRestoration = 'manual'
if your <router-view> resides within a <transition>:

scrollBehavior: async (to, from, savedPosition) => {
      if (savedPosition) {
        return savedPosition
      }

      const findEl = async (hash, x) => {
        return document.querySelector(hash) ||
          new Promise((resolve, reject) => {
            if (x > 50) {
              return resolve()
            }
            setTimeout(() => { resolve(findEl(hash, ++x || 1)) }, 100)
          })
      }

      if (to.hash) {
        let el = await findEl(to.hash)
        if ('scrollBehavior' in document.documentElement.style) {
          return window.scrollTo({ top: el.offsetTop, behavior: 'smooth' })
        } else {
          return window.scrollTo(0, el.offsetTop)
        }
      }

      return { x: 0, y: 0 }
    }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ways to interact with similar dynamic controls in Javascript

I have an aspx page with a Select box control: <select name="selViewPerPage" id="selViewPerPage" style="width:30px"> To ensure consistent styling across all browsers, I am replacing this html control with a dynamic select box using "selectBox.js". ...

Filter and search JSON data using React Native

Recently I have started learning about react-native and I am currently utilizing it for my school assignment. Prior to this, I was working with ionic. My current task involves filtering data that is stored in JSON format. I'm curious to know if react ...

What is the process for including points to the scoreboard?

I am currently working on a simple JavaScript game as my very first project. The game involves clicking a button to generate random numbers for both the user and computer, and the winner is determined by the larger number. I have set up a scoreboard to kee ...

Is there a more efficient method for transferring data between dynamically loaded components?

My recent project involved creating a small application using Vue.js and Express.js. To prepare the necessary components from the server, such as the combobox for Article.Category and Article.User, I needed to render options from the server. I utilized < ...

encountering the issue of not being able to assign a parameter of type 'string | undefined' to a parameter of type

Seeking help with the following issue: "Argument of type 'string | undefined' is not assignable to parameter of type" I am unsure how to resolve this error. Here is the section of code where it occurs: export interface IDropDown { l ...

Deleting elements from arrayA that do not exist in arrayB using JavaScript

Seeking guidance on a small project of mine. For example, I have these two arrays: chosenItems = ['apple', 'banana', 'cherry', 'date', 'kiwi'] availableFruits = ['apple', 'cherry', &ap ...

Unable to access placeholder information from the controller

I am new to implementing the mean stack. I attempted to view data from the controller, but encountered an error message in the web browser's console. Error: [$controller:ctrlreg] http://errors.angularjs.org/1.6.3/$controller/ctrlreg?p0=AppCtrl Stack ...

Can a graph effectively showcase data across a sporadically ordered timeline?

I have a collection of data stored in a database. While I have come across jpgraph during my research, I found that it may not be ideal for handling random time-sequencing and I am not a fan of their gantt chart layout. Do you have any recommendations for ...

Utilize the HTTP path to designate the currently active tab

Here is a sample code snippet for vertical tabs in React using the Material-UI library: import * as React from 'react'; import Tabs from '@mui/material/Tabs'; import Tab from '@mui/material/Tab'; import Typography from '@ ...

Sending dynamic data through AJAX to a CodeIgniter controller is a common task that allows for seamless

Can anyone help me with retrieving data from a looping form in CodeIgniter? The form works fine, but I'm struggling to fetch the looping data in the controller. Here's my view (form): <form action="#" id="ap_data"> <div class="table-r ...

What is the recommended method for establishing synchronous communication between the client and server during calls?

My client-server communication involves making multiple calls where each call depends on the previous one to finish and return a value before initiating the next one. Below is a simplified version of my current approach: Client: function doOrder() { v ...

Getting event arguments and scoped variable in event handler in Vue 2

Trying to implement a custom event handler in Vue 2 that takes arguments from a component as well as the current index (from v-for). Various methods have been found for achieving this (credit to Vue 2 arguments in inline (in-template) event handler), but ...

Exploring the possibilities: Establishing a Connection between Node.js and MySQL

I encountered an issue while attempting to connect node.js to MySQL. Despite having installed MySQL and the necessary libraries, I am unable to establish a connection. How can I troubleshoot this error? Additionally, what is the best approach for retrievin ...

Changing the default yarn registry for a specific project

Typically, I fetch packages from the internal server by using the command: yarn config set registry http://custom-packages-server.com However, for a new project, I want to switch back to the default registry without affecting other projects. If I try cha ...

Problems Arising with Javascript Animation Functionality

I've created a script for an interactive "reel" that moves up or down when clicking on specific arrow buttons. However, I'm encountering two issues: 1) The up arrow causes it to move downward, while the down arrow moves it upward. 2) After exe ...

Is there a way to adjust the parameters of objects within my scene that were loaded using OBJMTLLoader?

I am working on a scene that includes 3 cubes and a DAT.GUI menu. My goal is to switch any cube to wireframe mode when it is selected in the menu individually. Although my code is successful for 2 out of the 3 cubes, I am facing an issue where the first c ...

ngOptions compare by actual value

My webserver (node.js) serves a JSON file with a list of languages in the format: { "en" : "English", "fr" : "French" } and a separate JSON settings dictionary like this: { "currentLanguage" : "en" }. The select statement is as follows: <select ng-opti ...

Tips for solving the "jQuery needs a window with a document" error when using import statements

I'm encountering the "jQuery requires a window with a document" error, and it appears that I require guidance similar to this solution: Error: jQuery requires a window with a document My quest is to find the precise syntax for addressing this using i ...

Tips for making objects rotate vertically on their axis and orbit around another object in three.js

Thank you in advance for all the support as I've been asking a lot of questions about my project. My project involves creating a simulation where a planet earth and moon rotate around a sun. (They're not exactly rotating around the sun, but more ...

Unable to iterate over an array within a single file Vue component

I created a vue.js app utilizing single file components. In the data section, I initialized an empty array like this: data: function () { return { teamKeys: [] } }, Next, I populate this array by pushing values into it within one of my methods: ...