如何滚动和跟踪固定高度可滚动侧边栏上链接的活动状态?

How to scroll and follow active state on links on a fixed height scrollable sidebar?

我正在设计一个博客布局,它的内容在左侧,'table of contents' 边栏在右侧。侧边栏固定为全视口高度,但可以滚动以防里面的项目太多。

还值得注意的是,边栏中 link 的活动状态会根据它们在视口中的相应内容而变化。我的意思是,只要博客 post 在视口中,它的 link 就会在边栏中处于活动状态。它是由 Intersection Observer API.

完成的

现在除了有一个问题外,核心功能工作正常。

博客post多的时候,侧边栏也会有很多link。所以底部的 links 在侧边栏上自然是不可见的,因为它们在底部并且在滚动条被拉下之前是看不到的。因此,活动状态也不可见。

假设用户尝试阅读博客 post Text 8,相应的 Text 8 link 应该在边栏上可见,但实际上没有。在演示中最多只能看到 Text 7(基于我的视口)。

我想实现的是如何根据用户正在阅读的博客 post 上下移动滚动条?我的意思是,如果用户正在阅读文本 8,那么侧边栏将向下滚动并显示文本 8 link。如果他正在阅读文本 9,它将滚动到文本 9 link。现在,如果他决定从文本 9 读取文本 7,那么滚动条将向上移动两个位置并显示文本 7 link.

我不知道我是否可以正确解释它,但这是我能写的最好的。

如果你能帮我解决这个问题,那将是一个很大的帮助。

这是 codepen

这是片段:

const asideContent = $('#aside-content .aside-content');
const asideContentItem = $('#aside-content a');

const callback = (entries, observer) => {
  $(entries).each((idx, item) => {
    const navItem = $('#' + item.target.id);

    if (item.isIntersecting) {
      $(asideContentItem).each((i, eachLink) => {
        if ($(eachLink).attr("href") === ('#' + $(navItem).attr('id'))) {
          $(eachLink).addClass('active');
        } else {
          $(eachLink).removeClass('active');
        }
      })
    }
  })
};

const options = {
  threshold: 0.2
};

const observer = new IntersectionObserver(callback, options);
const container = $('#main');
const targetElements = $('.main-content .inner-container');

$(targetElements).each((idx, item) => {
  observer.observe(item);
});
.header-content {
  background-color: blue;
  height: 800px;
  margin: 20px 0;
}

#main {
  margin: 20px auto;
}

.main-content {
  background-color: cyan;
}

#aside-content {
  position: sticky;
  top: 0;
  align-self: flex-start;
  background-color: red;
  height: 100vh;
  overflow-y: auto;
}

#aside-content a {
  display: block;
  margin: 40px auto;
  text-decoration: none;
}

#aside-content a.active {
  background-color: rgba(0, 255, 0, 0.5);
}

.aside-fixed {
  position: fixed;
  top: 0;
  right: 0;
}

footer {
  background-color: brown;
  height: 200px;
}
<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/litera/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Test</a>
</nav>


<section class="container">
  <div class="row">
    <div class="col-md-12">
      <div class="header-content">
        <h3>header content</h3>
      </div>
    </div>
  </div>
</section>

<section class="container" id="main">
  <div class="row">

    <div class="col-8">
      <div class="main-content">
        <div id="text1" class="inner-container">
          <h2>Text 1</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit.
        </div>

        <div id="text2" class="inner-container">
          <h2>Text 2</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harumti aspernatur delectus mollitia libero similique assumenda quos sequi eligendi?
        </div>

        <div id="text3" class="inner-container">
          <h2>Text 3</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi, maxime repellat aperiam labore exercitationem enim possimus. Suscipit facilis debitis quidem excepturi?
        </div>

        <div id="text4" class="inner-container">
          <h2>Text 4</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text5" class="inner-container">
          <h2>Text 5</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text6" class="inner-container">
          <h2>Text 6</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text7" class="inner-container">
          <h2>Text 7</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text8" class="inner-container">
          <h2>Text 8</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text9" class="inner-container">
          <h2>Text 9</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>
      </div>
    </div>

    <div class="col-4" id="aside-content">
      <div class="aside-content">
        <h3>Table of Contents</h3>
        <div>
          <a href="#text1">Text 1</a>
          <a href="#text2">Text 2</a>
          <a href="#text3">Text 3</a>
          <a href="#text4">Text 4</a>
          <a href="#text5">Text 5</a>
          <a href="#text6">Text 6</a>
          <a href="#text7">Text 7</a>
          <a href="#text8">Text 8</a>
          <a href="#text9">Text 9</a>
        </div>
      </div>

    </div>
</section>

<footer>
  <h3>footer</h3>
</footer>

您可以通过在活动 class 更改时将滚动条位置设置为特定偏移量来解决此问题。这可以通过编辑您的 JS 并将滚动逻辑添加到添加 addClass('active') 部分来实现。

基本上你必须更新滚动条位置,因为活动 class 被添加到任何 link。

document.querySelector("#aside-content").scrollTo(0,position)

以及滚动条要设置的位置,可以从当前活动元素s/link的偏移量中获取。

document.querySelector("#aside-content > div > div > a.active").offsetTop

因此在添加 class 之后,上述步骤就可以完成了

$(eachLink).addClass('active');

var position = document.querySelector("#aside-content > div > div > a.active").offsetTop;

document.querySelector("#aside-content").scrollTo(0,position);

但是,上面的代码总是会设置位置,因此一开始 link 会将滚动设置为自身,并且不会让“table of contents”文本永远可见.要解决这种情况,可以通过检查活动元素是否是滚动的第一个子元素来使更新逻辑成为有条件的 div.

因此,整体工作代码应如下所示:

const asideContent = $('#aside-content .aside-content');
const asideContentItem = $('#aside-content a');

const callback = (entries, observer) => {
  $(entries).each((idx, item) => {
    const navItem = $('#' + item.target.id);

    if (item.isIntersecting) {
      $(asideContentItem).each((i, eachLink) => {
        if ($(eachLink).attr("href") === ('#' + $(navItem).attr('id'))) {
          $(eachLink).addClass('active');

          if (document.querySelector("#aside-content > div > div > a.active").parentNode.firstElementChild != document.querySelector("#aside-content > div > div > a.active")) {
            document.querySelector("#aside-content").scrollTo(0, document.querySelector("#aside-content > div > div > a.active").offsetTop);
          } else {
            document.querySelector("#aside-content").scrollTo(0, 0);
          }


        } else {
          $(eachLink).removeClass('active');
        }
      })
    }
  })
};

const options = {
  threshold: 0.2
};

const observer = new IntersectionObserver(callback, options);
const container = $('#main');
const targetElements = $('.main-content .inner-container');

$(targetElements).each((idx, item) => {
  observer.observe(item);
});
.header-content {
  background-color: blue;
  height: 800px;
  margin: 20px 0;
}

#main {
  margin: 20px auto;
}

.main-content {
  background-color: cyan;
}

#aside-content {
  position: sticky;
  top: 0;
  align-self: flex-start;
  background-color: red;
  height: 100vh;
  overflow-y: auto;
}

#aside-content a {
  display: block;
  margin: 40px auto;
  text-decoration: none;
}

#aside-content a.active {
  background-color: rgba(0, 255, 0, 0.5);
}

.aside-fixed {
  position: fixed;
  top: 0;
  right: 0;
}

footer {
  background-color: brown;
  height: 200px;
}
<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/litera/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Test</a>
</nav>


<section class="container">
  <div class="row">
    <div class="col-md-12">
      <div class="header-content">
        <h3>header content</h3>
      </div>
    </div>
  </div>
</section>

<section class="container" id="main">
  <div class="row">

    <div class="col-8">
      <div class="main-content">
        <div id="text1" class="inner-container">
          <h2>Text 1</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit.
        </div>

        <div id="text2" class="inner-container">
          <h2>Text 2</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harumti aspernatur delectus mollitia libero similique assumenda quos sequi eligendi?
        </div>

        <div id="text3" class="inner-container">
          <h2>Text 3</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi, maxime repellat aperiam labore exercitationem enim possimus. Suscipit facilis debitis quidem excepturi?
        </div>

        <div id="text4" class="inner-container">
          <h2>Text 4</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text5" class="inner-container">
          <h2>Text 5</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text6" class="inner-container">
          <h2>Text 6</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text7" class="inner-container">
          <h2>Text 7</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text8" class="inner-container">
          <h2>Text 8</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>

        <div id="text9" class="inner-container">
          <h2>Text 9</h2>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet provident recusandae, omnis laborum ad, pariatur debitis hic velit magni, expedita dicta vel nemo asperiores aperiam quia soluta esse consequuntur! Praesentium voluptate delectus, consequatur
          dolore veniam vitae sint error ea, facere nisi reprehenderit harum doloremque asperiores repudiandae eligendi.
        </div>
      </div>
    </div>

    <div class="col-4" id="aside-content">
      <div class="aside-content">
        <h3>Table of Contents</h3>
        <div>
          <a href="#text1">Text 1</a>
          <a href="#text2">Text 2</a>
          <a href="#text3">Text 3</a>
          <a href="#text4">Text 4</a>
          <a href="#text5">Text 5</a>
          <a href="#text6">Text 6</a>
          <a href="#text7">Text 7</a>
          <a href="#text8">Text 8</a>
          <a href="#text9">Text 9</a>
        </div>
      </div>

    </div>
</section>

<footer>
  <h3>footer</h3>
</footer>