六一的部落格


关关难过关关过,前路漫漫亦灿灿。




说明

  1. 监听滚动事件, 触发定时器, 到时实现文章目录滚动跟随
  2. 浏览器支持
    - docs-toc scrollTop my-toc scrollTop 文章目录跳转
    Safari O X -
    Firefox O X -
    Chrome O O docs-toc需要定时器, 延时要求大于文章目录跳转最大耗时

滚动监听

  1. window添加事件监听
    1window.addEventListener('scroll', () => {
    2    // 处理
    3  });
  2. window注册滚动处理
    1window.onscroll = function () {
    2    // 处理
    3};

进入判断

1document.addEventListener('DOMContentLoaded', () => {
2  const myToc = document.querySelector('.my-toc');
3  const fullToc = document.querySelector('.docs-toc');
4  if (!myToc || !fullToc) return;
5
6  // 后续处理  
7});

滚动计算


为标题排序, 厘清高亮标题在文章目录中的位置

1let i = 0;
2myToc.querySelectorAll('a').forEach(entry => {
3    entry.setAttribute('scrollIdx', i++);
4});

文章目录结构

.my-toc嵌套在.docs-toc中, .docs-toc多一个"文章目录"提示

  1. 根据.my-toc的滚动高度和其容纳的标题个数计算单个标题的高度
    1function computeHeadingHeight() {
    2  const toc = document.querySelector('.my-toc');
    3  return toc.scrollHeight / toc.querySelectorAll('a').length;
    4}
  2. 计算窗口能容纳的标题数,经过测试,取其1/6,可以使高亮标题始终处于文章目录偏上位置
    1function computeUpIdx() {
    2  const fullToc = document.querySelector('.docs-toc');
    3  const myToc = document.querySelector('.my-toc');
    4  const offset = fullToc.scrollHeight - myToc.scrollHeight;
    5  const max = parseInt((window.innerHeight - offset) / HeadingHeight);
    6  return parseInt(max / 6);
    7}
  3. 添加全局变量, 保存单个标题高度和高亮标题理想位置
    1let HeadingHeight, UpIdx;
    2
    3HeadingHeight = computeHeadingHeight();
    4UpIdx = computeUpIdx();

注册监听处理:重启定时器, 延时滚动文章目录

1let FollowTimer = null; // 全局
2const FollowTimerInterval = 300;
3
4window.onscroll = function () {
5    clearTimeout(FollowTimer);
6    FollowTimer = setTimeout(scrollFollow, FollowTimerInterval);
7};

实现文章目录滚动

无高亮标题, 不作处理;多个标题高亮时, 基于第一个标题计算滚动偏移

1function scrollFollow() {
2  const activeHeadings = document.querySelector('.my-toc').querySelectorAll('a.active');
3  if (activeHeadings.length > 0) {
4    const heading = activeHeadings.item(0);
5    const idx = heading.getAttribute('scrollIdx');
6    const scrollTarget = idx - upIdx;
7    document.querySelector('.docs-toc').scrollTop = HeadingHeight * scrollTarget;
8  }
9}

完整JavaScript代码

 1let HeadingHeight, UpIdx;
 2let FollowTimer = null;
 3const FollowTimerInterval = 300;
 4
 5function computeHeadingHeight() {
 6  const toc = document.querySelector('.my-toc');
 7  return toc.scrollHeight / toc.querySelectorAll('a').length;
 8}
 9
10function computeUpIdx() {
11  const fullToc = document.querySelector('.docs-toc');
12  const myToc = document.querySelector('.my-toc');
13  const offset = fullToc.scrollHeight - myToc.scrollHeight;
14  const max = parseInt((window.innerHeight - offset) / HeadingHeight);
15  return parseInt(max / 6);
16}
17
18function scrollFollow() {
19  const activeHeadings = document.querySelector('.my-toc').querySelectorAll('a.active');
20  if (activeHeadings.length > 0) {
21    const heading = activeHeadings.item(0);
22    const idx = heading.getAttribute('scrollIdx');
23    const scrollTarget = idx - UpIdx;
24    document.querySelector('.docs-toc').scrollTop = HeadingHeight * scrollTarget;
25  }
26}
27
28document.addEventListener('DOMContentLoaded', () => {
29  const myToc = document.querySelector('.my-toc');
30  const fullToc = document.querySelector('.docs-toc');
31  if (!myToc || !fullToc) return;
32
33  let i = 0;
34  myToc.querySelectorAll('a').forEach(entry => {
35    entry.setAttribute('scrollIdx', i++);
36  });
37
38  HeadingHeight = computeHeadingHeight();
39  UpIdx = computeUpIdx();
40
41  window.onscroll = function () {
42    clearTimeout(FollowTimer);
43    FollowTimer = setTimeout(scrollFollow, FollowTimerInterval);
44  };
45});

文章目录跟随文章内容滚动



说明

  1. 监听滚动事件, 触发定时器, 到时实现文章目录滚动跟随
  2. 浏览器支持
    - docs-toc scrollTop my-toc scrollTop 文章目录跳转
    Safari O X -
    Firefox O X -
    Chrome O O docs-toc需要定时器, 延时要求大于文章目录跳转最大耗时

滚动监听

  1. window添加事件监听
    1window.addEventListener('scroll', () => {
    2    // 处理
    3  });
  2. window注册滚动处理
    1window.onscroll = function () {
    2    // 处理
    3};

进入判断

1document.addEventListener('DOMContentLoaded', () => {
2  const myToc = document.querySelector('.my-toc');
3  const fullToc = document.querySelector('.docs-toc');
4  if (!myToc || !fullToc) return;
5
6  // 后续处理  
7});

滚动计算


为标题排序, 厘清高亮标题在文章目录中的位置

1let i = 0;
2myToc.querySelectorAll('a').forEach(entry => {
3    entry.setAttribute('scrollIdx', i++);
4});

文章目录结构

.my-toc嵌套在.docs-toc中, .docs-toc多一个"文章目录"提示

  1. 根据.my-toc的滚动高度和其容纳的标题个数计算单个标题的高度
    1function computeHeadingHeight() {
    2  const toc = document.querySelector('.my-toc');
    3  return toc.scrollHeight / toc.querySelectorAll('a').length;
    4}
  2. 计算窗口能容纳的标题数,经过测试,取其1/6,可以使高亮标题始终处于文章目录偏上位置
    1function computeUpIdx() {
    2  const fullToc = document.querySelector('.docs-toc');
    3  const myToc = document.querySelector('.my-toc');
    4  const offset = fullToc.scrollHeight - myToc.scrollHeight;
    5  const max = parseInt((window.innerHeight - offset) / HeadingHeight);
    6  return parseInt(max / 6);
    7}
  3. 添加全局变量, 保存单个标题高度和高亮标题理想位置
    1let HeadingHeight, UpIdx;
    2
    3HeadingHeight = computeHeadingHeight();
    4UpIdx = computeUpIdx();

注册监听处理:重启定时器, 延时滚动文章目录

1let FollowTimer = null; // 全局
2const FollowTimerInterval = 300;
3
4window.onscroll = function () {
5    clearTimeout(FollowTimer);
6    FollowTimer = setTimeout(scrollFollow, FollowTimerInterval);
7};

实现文章目录滚动

无高亮标题, 不作处理;多个标题高亮时, 基于第一个标题计算滚动偏移

1function scrollFollow() {
2  const activeHeadings = document.querySelector('.my-toc').querySelectorAll('a.active');
3  if (activeHeadings.length > 0) {
4    const heading = activeHeadings.item(0);
5    const idx = heading.getAttribute('scrollIdx');
6    const scrollTarget = idx - upIdx;
7    document.querySelector('.docs-toc').scrollTop = HeadingHeight * scrollTarget;
8  }
9}

完整JavaScript代码

 1let HeadingHeight, UpIdx;
 2let FollowTimer = null;
 3const FollowTimerInterval = 300;
 4
 5function computeHeadingHeight() {
 6  const toc = document.querySelector('.my-toc');
 7  return toc.scrollHeight / toc.querySelectorAll('a').length;
 8}
 9
10function computeUpIdx() {
11  const fullToc = document.querySelector('.docs-toc');
12  const myToc = document.querySelector('.my-toc');
13  const offset = fullToc.scrollHeight - myToc.scrollHeight;
14  const max = parseInt((window.innerHeight - offset) / HeadingHeight);
15  return parseInt(max / 6);
16}
17
18function scrollFollow() {
19  const activeHeadings = document.querySelector('.my-toc').querySelectorAll('a.active');
20  if (activeHeadings.length > 0) {
21    const heading = activeHeadings.item(0);
22    const idx = heading.getAttribute('scrollIdx');
23    const scrollTarget = idx - UpIdx;
24    document.querySelector('.docs-toc').scrollTop = HeadingHeight * scrollTarget;
25  }
26}
27
28document.addEventListener('DOMContentLoaded', () => {
29  const myToc = document.querySelector('.my-toc');
30  const fullToc = document.querySelector('.docs-toc');
31  if (!myToc || !fullToc) return;
32
33  let i = 0;
34  myToc.querySelectorAll('a').forEach(entry => {
35    entry.setAttribute('scrollIdx', i++);
36  });
37
38  HeadingHeight = computeHeadingHeight();
39  UpIdx = computeUpIdx();
40
41  window.onscroll = function () {
42    clearTimeout(FollowTimer);
43    FollowTimer = setTimeout(scrollFollow, FollowTimerInterval);
44  };
45});