高性能网站建设进行指南,沪佳装饰门店地址,济南建设工程招投标管理网,国外物流公司网站模板1 开发目标
目录导航组件旨在提供一个滚动目录导航功能#xff0c;使得用户可以方便地通过点击目录条目快速定位到对应的内容标题位置#xff0c;同时也能够随着滚动条的移动动态显示当前位置在目录中的位置#xff1a; 2 详细需求
2.1 标题提取与目录生成
组件需要能够自…1 开发目标
目录导航组件旨在提供一个滚动目录导航功能使得用户可以方便地通过点击目录条目快速定位到对应的内容标题位置同时也能够随着滚动条的移动动态显示当前位置在目录中的位置 2 详细需求
2.1 标题提取与目录生成
组件需要能够自动提取网页内容中的所有标题元素如 h1, h2, h3 等。提取的标题需要按照其在网页中的层级关系如 h1 后面跟着的 h2 是其子章节进行组织形成一个目录容器。目录容器需以清晰、直观的方式展示给用户允许用户通过点击目录条目进行导航。
2.2 滚动定位
当用户在目录容器中点击某个目录条目时网页的滚动条需要动态移动到对应的标题位置使得该标题出现在页面的最上方。滚动过程应该平滑且快速提升用户体验。
2.3 滚动条与目录条目交互
当用户滚动网页的滚动条时目录容器中的对应目录条目应该能够实时更新状态以指示当前所在位置。当滚动条经过某个标题时对应的目录条目应改变颜色如高亮显示以提醒用户当前的位置。
2.4 无滚动条情况处理
如果网页内容较少没有出现滚动条那么点击目录条目时不应触发任何滚动动作。这种情况下目录容器仍应正常显示以供用户浏览网页内容的结构。
3 代码实现
首先创建一个 neat_directory.js 文件该文件用于本组件的工具类、目录处理函数的代码构建。
1在具体的业务代码编写之前先实现一个工具类以及一些工具方法方便后面调用
class CommonUtil {// 设置 DIV 中的文字为垂直居中static centerYTextInDiv(container) {container.style.display flex;container.style.justifyContent center;container.style.flexDirection column;}// 判断 DIV 有无垂直滚动条static hasScrollbar(container) {var divStyle window.getComputedStyle(container);var isOverflowing container.scrollHeight container.clientHeight;var isScrollbarVisible isOverflowing (divStyle.overflow scroll || divStyle.overflow auto || divStyle.overflowY scroll || divStyle.overflowY auto);return isScrollbarVisible;}
}2接下来开始定义目录节点类型目录节点显示在目录区域
class DirectoryNode {static LEVEL_OFFSET 20; // 每个级别的目录节点偏移像素static NODE_HEIGHT 30px; // 目录节点高度static NODE_NAME_FONTSIZE 14px; // 默认目录标题字符串的字体大小static NODE_NAME_COLOR #000; // 默认目录标题字符串字体颜色static NODE_NAME_ACTIVE_COLOR red; // 默认目录标题在激活情况下字符串字体颜色constructor(container, para) {this.container container; // 本目录节点的容器this.para para; // 配置参数包含页面内容的容器、标题容器以及标题等级等this.init();}上面代码定义了 DirectoryNode 的一些默认属性与成员变量并且创建构造函数该函数接收调用者传入的 DIV 容器并且调用 render 方法。 在 render 方法中需要渲染当前目录节点并且还要定义点击事件 render() {this.container.style.width 100%;this.container.style.height this.para.height ?? DirectoryNode.NODE_HEIGHT;this.container.style.fontSize this.para.fontSize ?? DirectoryNode.NODE_NAME_FONTSIZE;this.container.style.color this.para.color ?? DirectoryNode.NODE_NAME_COLOR;this.container.innerText this.para.name;if (this.para.level 1) { // 设置目录节点偏移this.container.style.paddingLeft ((this.para.level - 1) * DirectoryNode.LEVEL_OFFSET) px;}this.container.style.cursor pointer;// 点击事件let that this;this.container.onclick function () {that.para.onClick.call(that.para.onClickObj, that);}}然后需要对目录节点的激活与非激活状态以及目录跳转逻辑做实现 // 目录节点激活并跳转对应目录位置activate() {this.container.style.color DirectoryNode.NODE_NAME_ACTIVE_COLOR;}// 目录节点非激活deactivate() {this.container.style.color this.para.color ?? DirectoryNode.NODE_NAME_COLOR;}// 目录跳转jump() {// 计算目标元素相对于父元素的位置 let targetElementRect this.para.titleContainer.getBoundingClientRect();let parentRect this.para.contentContainer.getBoundingClientRect();// 滚动到目标元素的顶部let offset targetElementRect.top - parentRect.top this.para.contentContainer.scrollTop;this.para.contentContainer.scrollTop offset;}// 获取在页面内容的容器中当前目录节点所对应的标题元素离顶部的距离getTopOffset() {let targetElementRect this.para.titleContainer.getBoundingClientRect();let parentRect this.para.contentContainer.getBoundingClientRect();return targetElementRect.top parentRect.top;}
}3在完成 DirectoryNode 的实现以后开始创建目录类型 Directory
class Directory {constructor(container, para) {this.container container; // 传入的目录容器用于渲染提取生成的目录this.para para; // 配置参数包含页面内容的容器this.nodes []; // 目录节点集合this.jumpFlagfalse; // 当前是否处于点击目录节点进行跳转的状态this.render();}目录类型 Directory 的渲染函数 render 主要是获取页面内容中所有节点遍历处理标题元素然后创捷目录节点。此后还需要定义页面内容的容器在滚动滚动轴时触发目录变化的逻辑 render() {// 清空目录容器this.container.innerHTML ;// 获取页面内容中所有节点遍历处理标题元素let containerNodes this.para.contentContainer.childNodes;containerNodes.forEach(element {if (!element.tagName) {return;}let tagName element.tagName.toUpperCase();if (2 tagName.length) {let tagName1 tagName.slice(0, 1);let tagName2 tagName.slice(1, 2);if (H tagName1 !isNaN(tagName2)) {let level parseInt(tagName2); // 标题等级let directoryNodeContainer document.createElement(div);this.container.appendChild(directoryNodeContainer);let nodePara {name: element.innerText,level: level,titleContainer: element,contentContainer: this.para.contentContainer,onClick: this.jumpTo,onClickObj: this,}let node new DirectoryNode(directoryNodeContainer, nodePara);this.nodes.push(node);}}});// 页面内容的容器在滚动滚动轴时触发目录变化let that this;this.para.contentContainer.addEventListener(scroll, function () {// 如果网页内容较少没有出现滚动条那么页面内容的容器在滚动滚动轴时不做任何触发// 如果当前是处于点击目录节点进行跳转的状态则不做处理if (!CommonUtil.hasScrollbar(that.para.contentContainer) || that.jumpFlag) {return;}// 判断当前内容属于哪一个目录节点let activeNodenull;for (let index 0; index that.nodes.length; index) {const node that.nodes[index];if(node.getTopOffset()0 index1that.nodes.length that.nodes[index1].getTopOffset()0){activeNode node;break;}}if(null activeNode that.nodes.length0){activeNode that.nodes[0];}that.nodes.forEach(element {element.deactivate();});activeNode.activate();});}在完成渲染函数 render 的实现后即要实现点击后目录跳转的功能注意如果网页内容较少没有出现滚动条那么点击目录条目时不应触发任何滚动动作 // 目录跳转jumpTo(node) {// 如果网页内容较少没有出现滚动条那么点击目录条目时不应触发任何滚动动作if (!CommonUtil.hasScrollbar(this.para.contentContainer)) {return;}this.jumpFlagtrue; this.nodes.forEach(element {element.deactivate();});node.activate();node.jump();// 延迟一段时间let that this;setTimeout(function() { that.jumpFlagfalse; }, 100); }
}至此整个目录导航功能的组件构建结束。
4完成目录导航功能的组件的代码编写后可以创建 neat_directory.html 文件调用该组件
!DOCTYPE html
htmlheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /meta http-equivX-UA-Compatible contentieedge /titleheader tab/titlestylehtml {height: 100%;}body {margin: 0;height: 100%;}/style
/headbodydiv iddivMain styleheight: 100%;width: 100%;display: flex;div iddivDirectory stylemargin:10px;height: 500px;width: 300px;border: 1px solid #aaa;padding: 10px;/divdiv iddivContent stylemargin:10px;height: 500px;width: 600px;border: 1px solid #aaa;padding: 10px;overflow-y: auto;h11 第一章/h1h21.1 第一章 第一节 /h2span组件需要能够自动提取网页内容中的所有标题元素如 h1, h2, h3 等。/spanspan提取的标题需要按照其在网页中的层级关系如 h1 后面跟着的 h2 是其子章节进行组织形成一个目录容器。/spanspan目录容器需以清晰、直观的方式展示给用户允许用户通过点击目录条目进行导航。/spanh31.1.1 第一章 第一节 第一段/h3span组件需要能够自动提取网页内容中的所有标题元素如 h1, h2, h3 等。/spanspan提取的标题需要按照其在网页中的层级关系如 h1 后面跟着的 h2 是其子章节进行组织形成一个目录容器。/spanspan目录容器需以清晰、直观的方式展示给用户允许用户通过点击目录条目进行导航。/spanh31.1.2 第一章 第一节 第二段/h3span组件需要能够自动提取网页内容中的所有标题元素如 h1, h2, h3 等。/spanspan提取的标题需要按照其在网页中的层级关系如 h1 后面跟着的 h2 是其子章节进行组织形成一个目录容器。/spanspan目录容器需以清晰、直观的方式展示给用户允许用户通过点击目录条目进行导航。/spanh12 第二章/h1h22.1 第二章 第一节 /h2span组件需要能够自动提取网页内容中的所有标题元素如 h1, h2, h3 等。/spanspan提取的标题需要按照其在网页中的层级关系如 h1 后面跟着的 h2 是其子章节进行组织形成一个目录容器。/spanspan目录容器需以清晰、直观的方式展示给用户允许用户通过点击目录条目进行导航。/spanh32.1.1 第二章 第一节 第一段/h3span组件需要能够自动提取网页内容中的所有标题元素如 h1, h2, h3 等。/spanspan提取的标题需要按照其在网页中的层级关系如 h1 后面跟着的 h2 是其子章节进行组织形成一个目录容器。/spanspan目录容器需以清晰、直观的方式展示给用户允许用户通过点击目录条目进行导航。/spanh32.1.2 第二章 第一节 第二段/h3span组件需要能够自动提取网页内容中的所有标题元素如 h1, h2, h3 等。/spanspan提取的标题需要按照其在网页中的层级关系如 h1 后面跟着的 h2 是其子章节进行组织形成一个目录容器。/spanspan目录容器需以清晰、直观的方式展示给用户允许用户通过点击目录条目进行导航。/span/div/div
/body
script src./neat_directory.js/script
scriptlet para {contentContainer:document.getElementById(divContent),}let directory new Directory(document.getElementById(divDirectory),para);/script/html