The anchor menu inside the scroll block does not work correctly

28 views Asked by At

There is a jQuery-code that animates a smooth transition from menu items to anchors. For the first menu items that are immediately visible, everything works fine. But from those menu items to which you need to scroll, the transition is performed incorrectly (to see the problem, the code shoud be run in a small snippet, not expanded to full screen):

$('.main-nav a').on('click',function(e){
  e.preventDefault();
  let elementClick = $(this).attr('href');
  let destination = $(elementClick).offset().top - $(elementClick).parent().offset().top - $(elementClick).parent().scrollTop();
  $('main').animate({ scrollTop: destination }, 1000);
});
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
body {
  height: 100vh;
  font-size: 16px;
}
header, footer {
  height: 20vh;
  border: 1px solid maroon;
  display: flex;
  justify-content: center;
  align-items: center;
}
main {
  height: 60vh;
  overflow: auto;
  padding: 20px;
}
.main-nav {
  display: flex;
  flex-direction: column;
  margin-bottom: 30px;
}
.main-nav a {
  padding: 5px 0;
}
.content {
  padding: 20px;
  color: white;
  height: 100vh;
}
#one, #four, #seven {
  background-color: red;
}
#two, #five, #eight {
  background-color: green;
}
#three, #six, #nine {
  background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<header>header</header>
<main>
  <div class="main-nav">
    <a href="#one">1. One</a>
    <a href="#two">2. Two</a>
    <a href="#three">3. Three</a>
    <a href="#four">4. Four</a>
    <a href="#five">5. Five</a>
    <a href="#six">6. Six</a>
    <a href="#seven">7. Seven</a>
    <a href="#eight">8. Eight</a>
    <a href="#nine">9. Nine</a>
  </div>
  <div class="content" id="one">One</div>
  <div class="content" id="two">Two</div>
  <div class="content" id="three">Three</div>
  <div class="content" id="four">Four</div>
  <div class="content" id="five">Five</div>
  <div class="content" id="six">Six</div>
  <div class="content" id="seven">Seven</div>
  <div class="content" id="eight">Eight</div>
  <div class="content" id="nine">Nine</div>
</main>
<footer>footer</footer>

Obviously the problem is with the variable `destination`. But I do not know how to calculate it correctly for all menu items.

1

There are 1 answers

0
CBroe On

If you position main relative, then

$(elementClick).position().top + $(elementClick).parent().scrollTop();

is enough to calculate the correct position to scroll to.

Difference between the methods, https://api.jquery.com/offset/#offset:

The .offset() method allows us to retrieve the current position of an element (specifically its border box, which excludes margins) relative to the document. Contrast this with .position(), which retrieves the current position relative to the offset parent.

By adding position: relative, main becomes the offset parent.

$('.main-nav a').on('click',function(e){
  e.preventDefault();
  let elementClick = $(this).attr('href');
  let destination = $(elementClick).position().top + $(elementClick).parent().scrollTop();
  $('main').animate({ scrollTop: destination }, 1000);
});
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
body {
  height: 100vh;
  font-size: 16px;
}
header, footer {
  height: 20vh;
  border: 1px solid maroon;
  display: flex;
  justify-content: center;
  align-items: center;
}
main {
  height: 60vh;
  overflow: auto;
  padding: 20px;
  position: relative;
}
.main-nav {
  display: flex;
  flex-direction: column;
  margin-bottom: 30px;
}
.main-nav a {
  padding: 5px 0;
}
.content {
  padding: 20px;
  color: white;
  height: 100vh;
}
#one, #four, #seven {
  background-color: red;
}
#two, #five, #eight {
  background-color: green;
}
#three, #six, #nine {
  background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<header>header</header>
<main>
  <div class="main-nav">
    <a href="#one">1. One</a>
    <a href="#two">2. Two</a>
    <a href="#three">3. Three</a>
    <a href="#four">4. Four</a>
    <a href="#five">5. Five</a>
    <a href="#six">6. Six</a>
    <a href="#seven">7. Seven</a>
    <a href="#eight">8. Eight</a>
    <a href="#nine">9. Nine</a>
  </div>
  <div class="content" id="one">One</div>
  <div class="content" id="two">Two</div>
  <div class="content" id="three">Three</div>
  <div class="content" id="four">Four</div>
  <div class="content" id="five">Five</div>
  <div class="content" id="six">Six</div>
  <div class="content" id="seven">Seven</div>
  <div class="content" id="eight">Eight</div>
  <div class="content" id="nine">Nine</div>
</main>
<footer>footer</footer>