LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

使用 HTML + JavaScript 实现页面引导功能(附完整代码)

admin
2025年12月26日 14:34 本文热度 1220
在现代 Web 应用中,新用户引导是提升用户体验的重要环节。通过页面引导功能,我们可以逐步向用户介绍界面功能,帮助他们快速熟悉应用操作。这种引导方式能够有效降低用户对操作系统的学习成本,提高产品接受度。本文将介绍如何使用 HTML、CSS 和 JavaScript 实现页面引导功能。

效果演示

本系统通过高亮特定元素并在其旁边显示说明信息,引导用户逐步了解页面功能。当引导启动后,页面背景会变暗,目标元素被高亮显示,提示框显示当前步骤的说明文字。用户可以通过“上一步”、“下一步”按钮在引导步骤间切换,或者点击"完成"按钮结束引导。引导组件还会自动处理滚动,确保目标元素始终可见。

页面结构

页面主要包含以下区域:引导按钮、内容卡片、超出页面的元素区域和边缘元素区域。引导组件启动时会由 JavaScript 生成覆盖层、高亮区域和提示框。
<button id="startGuide" class="btn">开始引导</button>
<div id="usageCard" class="card">  <h2>使用指南</h2></div>
<div class="extra-content">  <p>浏览以下内容了解我们的详细信息。</p>  <div id="scrollTestElement" style="margin-top: 800px; padding: 20px; background: #fff;">    <p>这里是关于我们的更多详细介绍和联系方式。</p>  </div></div>
<div class="edge-elements">  <div id="leftEdgeElement" style="position: fixed; left: 10px; top: 50%; transform: translateY(-50%);   background: #409eff; color: white; padding: 15px; width: 150px;">    <h4>左侧固定元素</h4>    <p>用于测试左侧引导</p>  </div>  <div id="rightEdgeElement" style="position: fixed; right: 10px; top: 50%; transform: translateY(-50%);   background: #67c23a; color: white; padding: 15px; width: 150px;">    <h4>右侧固定元素</h4>    <p>用于测试右侧引导</p>  </div></div>

核心功能实现

系统通过创建 PageGuide 类来管理引导流程,包含步骤配置、当前步骤索引、组件激活状态等属性。构造函数接收步骤数组作为参数,每个步骤包含目标元素选择器、标题和内容。
class PageGuide {  constructor(steps) {    this.steps = steps;    this.currentStep = 0;    this.isActive = false;    this.highlight = null;    this.tooltip = null;    this.overlay = null;    this.scrollHandler = null;    this.scrollTimer = null;  }}

创建引导元素

createElements 方法动态创建覆盖层、高亮区域和提示框元素,并将它们添加到文档体中。覆盖层用于遮罩整个页面,高亮区域突出显示目标元素,提示框显示引导内容。
createElements() {  // 创建 overlay 层  this.overlay = document.createElement('div');  this.overlay.className = 'guide-overlay';  document.body.appendChild(this.overlay);  // 创建高亮区域  this.highlight = document.createElement('div');  this.highlight.className = 'guide-highlight';  document.body.appendChild(this.highlight);  // 创建提示框  this.tooltip = document.createElement('div');  this.tooltip.className = 'guide-tooltip';  document.body.appendChild(this.tooltip);  // 添加滚动事件监听器  this.scrollHandler = this.handleScroll.bind(this);  window.addEventListener('scroll'this.scrollHandler, { passive: true });}

高亮区域定位

updateHighlightPosition 方法实现高亮区域的定位逻辑,通过计算目标元素的边界矩形,设置高亮框的位置和尺寸,确保与目标元素精确匹配。
updateHighlightPosition() {  const step = this.steps[this.currentStep];  const targetElement = document.querySelector(step.element);  if (!targetElement) {    console.warn(`Element ${step.element} not found`);    return;  }  const rect = targetElement.getBoundingClientRect();  const scrollTop = window.scrollY || document.documentElement.scrollTop;  const scrollLeft = window.scrollX || document.documentElement.scrollLeft;  // 设置高亮区域位置和尺寸  this.highlight.style.top = (rect.top + scrollTop - 4) + 'px';  this.highlight.style.left = (rect.left + scrollLeft - 4) + 'px';  this.highlight.style.width = (rect.width + 4) + 'px';  this.highlight.style.height = (rect.height + 4) + 'px';}

提示框定位

positionTooltip 方法根据目标元素位置智能计算提示框显示位置,自动选择最佳显示方向,并处理边界情况,避免提示框超出视窗。
positionTooltip(targetElement) {  const rect = targetElement.getBoundingClientRect();  const tooltipRect = this.tooltip.getBoundingClientRect();  const scrollTop = window.scrollY || document.documentElement.scrollTop;  const scrollLeft = window.scrollX || document.documentElement.scrollLeft;  const isFixed = window.getComputedStyle(targetElement).position === 'fixed';  const isNearLeft = rect.left < 50;  const isNearRight = rect.right > window.innerWidth - 50;  let top, left, arrowDir = '';  // 统一处理定位逻辑  if (isNearLeft && rect.right + tooltipRect.width + 20 < window.innerWidth) {    top = rect.top + scrollTop + (rect.height / 2) - (tooltipRect.height / 2);    left = (isFixed ? rect.right : rect.right + scrollLeft) + 10;    arrowDir = 'right';  } else if (isNearRight && rect.left - tooltipRect.width - 20 > 0) {    top = rect.top + scrollTop + (rect.height / 2) - (tooltipRect.height / 2);    left = (isFixed ? rect.left : rect.left + scrollLeft) - tooltipRect.width - 10;    arrowDir = 'left';  } else {    top = (isFixed ? rect.bottom : rect.bottom + scrollTop) + 10;    left = (isFixed ? rect.left : rect.left + scrollLeft) + (rect.width / 2) - (tooltipRect.width / 2);    arrowDir = 'bottom';    const maxPos = isFixed ? window.innerHeight : scrollTop + window.innerHeight;    if (top + tooltipRect.height > maxPos) {      top = (isFixed ? rect.top : rect.top + scrollTop) - tooltipRect.height - 10;      arrowDir = 'top';    }  }  // 边界检查  const isHorz = ['bottom''top'].includes(arrowDir);  if (isHorz) left = Math.max(10Math.min(left, window.innerWidth - tooltipRect.width - 10));  else top = Math.max(10Math.min(top, window.innerHeight - tooltipRect.height - 10));  // 应用样式  this.tooltip.style.top = top + 'px';  this.tooltip.style.left = left + 'px';  const arrow = this.tooltip.querySelector('#guideArrow');  arrow.className = 'guide-arrow';  arrow.classList.add(`guide-arrow-${arrowDir}`);}

扩展建议

    • 本地存储:实现引导状态本地存储,避免重复显示引导

    • 自动播放:增加自动播放模式,无需手动点击下一步

    • 快捷键控制:增加键盘快捷键控制,方向键、ESC键控制步骤切换与退出

    完整代码

    git地址:https://gitee.com/ironpro/hjdemo/blob/master/page-guide/index.html
    <!DOCTYPE html><html lang="zh-CN"><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>页面引导组件</title>  <style>      body {          margin0;          padding20px;          background#f8f9fa;      }      .container {          max-width800px;          margin0 auto;      }      .header {          text-align: center;          margin-bottom40px;      }      .card {          background: white;          padding24px;          margin-bottom20px;          box-shadow0 2px 8px rgba(0000.1);      }      .btn {          padding10px 20px;          background#409eff;          color: white;          border: none;          cursor: pointer;          font-size16px;      }      .btn:hover {          background#337ecc;      }      .extra-content {          background#f0f0f0;          margin-top20px;          padding20px;      }      /* 引导组件 */      .guide-overlay {          position: fixed;          top0;          left0;          width100%;          height100%;          background: transparent;          z-index9999;          pointer-events: auto;      }      .guide-highlight {          position: absolute;          border2px solid #409eff;          box-shadow0 0 0 9999px rgba(0000.7);          z-index10000;          pointer-events: none;          background: transparent;          transition: all 0.3s ease;      }      .guide-tooltip {          position: absolute;          background: white;          padding16px;          box-shadow0 4px 14px rgba(0000.15);          max-width300px;          z-index10001;          transition: all 0.3s ease;      }      .guide-tooltip-title {          font-size16px;          font-weight600;          margin0 0 8px 0;          color#333;      }      .guide-tooltip-content {          font-size14px;          color#666;          line-height1.5;          margin0 0 16px 0;      }      .guide-navigation {          display: flex;          align-items: center;          justify-content: space-between;      }      .guide-btn {          padding6px 14px;          border: none;          cursor: pointer;          font-size14px;          transition: background 0.2s;      }      .guide-btn-prev {          background#f5f5f5;          color#666;      }      .guide-btn-prev:hover {          background#e0e0e0;      }      .guide-btn-prev:disabled {          background#f5f5f5;          color#ccc;          cursor: not-allowed;      }      .guide-btn-prev:disabled:hover {          background#f5f5f5;      }      .guide-btn-next {          background#409eff;          color: white;      }      .guide-btn-next:hover {          background#337ecc;      }      .guide-btn-finish {          background#67c23a;          color: white;      }      .guide-btn-finish:hover {          background#55a130;      }      .guide-progress {          font-size14px;          color#999;          text-align: center;          margin0 10px;          white-space: nowrap;      }      .guide-close {          position: absolute;          top8px;          right8px;          background: none;          border: none;          font-size18px;          cursor: pointer;          color#999;      }      .guide-arrow {          position: absolute;          width0;          height0;          border-style: solid;          border-width8px;          z-index10002;      }      .guide-arrow-top {          top100%;          left50%;          transformtranslateX(-50%);          margin-top: -1px;          border-color: white transparent transparent transparent;          border-top-width8px;          border-bottom-width0;      }      .guide-arrow-bottom {          bottom100%;          left50%;          transformtranslateX(-50%);          margin-bottom: -1px;          border-color: transparent transparent white transparent;          border-bottom-width8px;          border-top-width0;      }      .guide-arrow-left {          left100%;          top50%;          transformtranslateY(-50%);          margin-left: -1px;          border-color: transparent transparent transparent white;          border-left-width8px;          border-right-width0;      }      .guide-arrow-right {          right100%;          top50%;          transformtranslateY(-50%);          margin-right: -1px;          border-color: transparent white transparent transparent;          border-right-width8px;          border-left-width0;      }  </style></head><body><div class="container">  <div class="header">    <h1>欢迎访问我们的网站</h1>    <button id="startGuide" class="btn">开始引导</button>  </div>  <div id="usageCard" class="card">    <h2>使用指南</h2>  </div>  <div class="extra-content">    <p>浏览以下内容了解我们的详细信息。</p>    <div id="scrollTestElement" style="margin-top: 800px; padding: 20px; background: #fff;">      <p>这里是关于我们的更多详细介绍和联系方式。</p>    </div>  </div>  <div class="edge-elements">    <div id="leftEdgeElement" style="position: fixed; left: 10px; top: 50%; transform: translateY(-50%);     background: #409eff; color: white; padding: 15px; width: 150px;">      <h4>左侧固定元素</h4>      <p>用于测试左侧引导</p>    </div>    <div id="rightEdgeElement" style="position: fixed; right: 10px; top: 50%; transform: translateY(-50%);     background: #67c23a; color: white; padding: 15px; width: 150px;">      <h4>右侧固定元素</h4>      <p>用于测试右侧引导</p>    </div>  </div></div><script>  class PageGuide {    constructor(steps) {      this.steps = steps;      this.currentStep = 0;      this.isActive = false;      this.highlight = null;      this.tooltip = null;      this.overlay = null;      this.scrollHandler = null;      this.scrollTimer = null;    }    // 创建引导元素    createElements() {      // 创建 overlay 层      this.overlay = document.createElement('div');      this.overlay.className = 'guide-overlay';      document.body.appendChild(this.overlay);      // 创建高亮区域      this.highlight = document.createElement('div');      this.highlight.className = 'guide-highlight';      document.body.appendChild(this.highlight);      // 创建提示框      this.tooltip = document.createElement('div');      this.tooltip.className = 'guide-tooltip';      document.body.appendChild(this.tooltip);      // 添加滚动事件监听器      this.scrollHandler = this.handleScroll.bind(this);      window.addEventListener('scroll'this.scrollHandler, { passivetrue });    }    // 处理滚动事件    handleScroll() {      if (this.isActive) {        requestAnimationFrame(() => {          this.updateHighlightPosition();          const step = this.steps[this.currentStep];          const targetElement = document.querySelector(step.element);          if (targetElement) {            this.positionTooltip(targetElement);          }        });      }    }    // 更新高亮区域位置    updateHighlightPosition() {      const step = this.steps[this.currentStep];      const targetElement = document.querySelector(step.element);      if (!targetElement) {        console.warn(`Element ${step.element} not found`);        return;      }      const rect = targetElement.getBoundingClientRect();      const scrollTop = window.scrollY || document.documentElement.scrollTop;      const scrollLeft = window.scrollX || document.documentElement.scrollLeft;      // 设置高亮区域位置和尺寸      this.highlight.style.top = (rect.top + scrollTop - 4) + 'px';      this.highlight.style.left = (rect.left + scrollLeft - 4) + 'px';      this.highlight.style.width = (rect.width + 4) + 'px';      this.highlight.style.height = (rect.height + 4) + 'px';    }    // 显示当前步骤    showStep(index) {      if (index < 0 || index >= this.steps.lengthreturn;      this.currentStep = index;      const step = this.steps[index];      const targetElement = document.querySelector(step.element);      if (!targetElement) {        console.warn(`Element ${step.element} not found`);        return;      }      // 自动滚动到目标元素位置      this.scrollToElement(targetElement);      // 更新高亮区域位置      this.updateHighlightPosition();      // 设置提示框内容      this.tooltip.innerHTML = `<button class="guide-close">×</button>        <div class="guide-arrow" id="guideArrow"></div>        <h3 class="guide-tooltip-title" id="guideTitle">${step.title}</h3>        <p class="guide-tooltip-content" id="guideContent">${step.content}</p>        <div class="guide-navigation">          <button class="guide-btn guide-btn-prev" id="guidePrev" ${index === 0 ? 'disabled' : ''}>上一步</button>          <div class="guide-progress">${index + 1} / ${this.steps.length}</div>          ${index < this.steps.length - 1 ? '<button class="guide-btn guide-btn-next" id="guideNext">下一步</button>' : '<button class="guide-btn guide-btn-finish" id="guideFinish">完成</button>'}        </div>`;      // 计算提示框位置      this.positionTooltip(targetElement);      // 绑定事件      this.bindEvents();    }    // 计算并设置提示框位置    positionTooltip(targetElement) {      const rect = targetElement.getBoundingClientRect();      const tooltipRect = this.tooltip.getBoundingClientRect();      const scrollTop = window.scrollY || document.documentElement.scrollTop;      const scrollLeft = window.scrollX || document.documentElement.scrollLeft;      const isFixed = window.getComputedStyle(targetElement).position === 'fixed';      const isNearLeft = rect.left < 50;      const isNearRight = rect.right > window.innerWidth - 50;      let top, left, arrowDir = '';      // 统一处理定位逻辑      if (isNearLeft && rect.right + tooltipRect.width + 20 < window.innerWidth) {        top = rect.top + scrollTop + (rect.height / 2) - (tooltipRect.height / 2);        left = (isFixed ? rect.right : rect.right + scrollLeft) + 10;        arrowDir = 'right';      } else if (isNearRight && rect.left - tooltipRect.width - 20 > 0) {        top = rect.top + scrollTop + (rect.height / 2) - (tooltipRect.height / 2);        left = (isFixed ? rect.left : rect.left + scrollLeft) - tooltipRect.width - 10;        arrowDir = 'left';      } else {        top = (isFixed ? rect.bottom : rect.bottom + scrollTop) + 10;        left = (isFixed ? rect.left : rect.left + scrollLeft) + (rect.width / 2) - (tooltipRect.width / 2);        arrowDir = 'bottom';        const maxPos = isFixed ? window.innerHeight : scrollTop + window.innerHeight;        if (top + tooltipRect.height > maxPos) {          top = (isFixed ? rect.top : rect.top + scrollTop) - tooltipRect.height - 10;          arrowDir = 'top';        }      }      // 边界检查      const isHorz = ['bottom''top'].includes(arrowDir);      if (isHorz) left = Math.max(10Math.min(left, window.innerWidth - tooltipRect.width - 10));      else top = Math.max(10Math.min(top, window.innerHeight - tooltipRect.height - 10));      // 应用样式      this.tooltip.style.top = top + 'px';      this.tooltip.style.left = left + 'px';      const arrow = this.tooltip.querySelector('#guideArrow');      arrow.className = 'guide-arrow';      arrow.classList.add(`guide-arrow-${arrowDir}`);    }    // 添加自动滚动到元素的方法    scrollToElement(element) {      const rect = element.getBoundingClientRect();      const computedStyle = window.getComputedStyle(element);      const isFixed = computedStyle.position === 'fixed';      // 对于固定定位元素,不需要滚动      if (isFixed) {        // 直接更新位置        setTimeout(() => {          this.updateHighlightPosition();          this.positionTooltip(element);        }, 100);        return;      }      const isVisible = (        rect.top >= 0 &&        rect.left >= 0 &&        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&        rect.right <= (window.innerWidth || document.documentElement.clientWidth)      );      // 如果元素不在视窗内,则滚动到元素位置      if (!isVisible) {        const scrollTop = window.scrollY || document.documentElement.scrollTop;        const elemTop = rect.top + scrollTop;        const offsetTop = elemTop - (window.innerHeight / 2) + (rect.height / 2);
            window.scrollTo({          top: offsetTop,          behavior'smooth'        });        // 等待滚动完成后更新位置        setTimeout(() => {          this.updateHighlightPosition();          this.positionTooltip(element);        }, 500);      }    }    // 绑定事件    bindEvents() {      // 上一步按钮      const prevBtn = this.tooltip.querySelector('#guidePrev');      if (prevBtn) {        prevBtn.addEventListener('click'() => {          if (!prevBtn.disabled) {            this.showStep(this.currentStep - 1);          }        });      }      // 下一步按钮      const nextBtn = this.tooltip.querySelector('#guideNext');      if (nextBtn) {        nextBtn.addEventListener('click'() => {          this.showStep(this.currentStep + 1);        });      }      // 完成按钮      const finishBtn = this.tooltip.querySelector('#guideFinish');      if (finishBtn) {        finishBtn.addEventListener('click'() => {          this.finish();        });      }      // 关闭按钮      const closeBtn = this.tooltip.querySelector('.guide-close');      if (closeBtn) {        closeBtn.addEventListener('click'() => {          this.finish();        });      }    }    // 启动引导    start() {      if (this.isActivereturn;      this.isActive = true;      this.createElements();      this.highlight.style.display = 'block';      this.tooltip.style.display = 'block';      this.showStep(0);    }    // 结束引导    finish() {      this.isActive = false;      // 移除滚动事件监听器      if (this.scrollHandler) {        window.removeEventListener('scroll'this.scrollHandler);      }      if (this.scrollTimer) {        clearTimeout(this.scrollTimer);      }      if (this.overlay) {        document.body.removeChild(this.overlay);        this.overlay = null;      }      if (this.highlight) {        document.body.removeChild(this.highlight);        this.highlight = null;      }      if (this.tooltip) {        document.body.removeChild(this.tooltip);        this.tooltip = null;      }      // 判断是否需要返回顶部      const scrollTop = window.scrollY || document.documentElement.scrollTop;      if (scrollTop > 100) {        window.scrollTo({top0behavior'smooth'});      }    }  }  // 初始化引导  document.addEventListener('DOMContentLoaded'function() {    // 定义引导步骤    const guideSteps = [      {element'#startGuide'title'欢迎使用引导功能'content'这是页面引导组件的演示。点击此按钮开始了解如何使用它。'},      {element'#usageCard'title'使用方法'content'使用非常简单,只需要定义步骤配置并启动引导即可。'},      {element'#leftEdgeElement'title'左侧定位演示'content'当元素靠近屏幕左侧时,提示框会自动显示在元素右侧。'},      {element'#rightEdgeElement'title'右侧定位演示'content'当元素靠近屏幕右侧时,提示框会自动显示在元素左侧。'},      {element'#scrollTestElement'title'滚动测试'content'即使页面滚动,引导组件也能正确显示高亮区域和提示框。'}    ];    // 绑定开始按钮事件    document.getElementById('startGuide').addEventListener('click'function() {      new PageGuide(guideSteps).start();    });  });</script></body></html>


    阅读原文:https://mp.weixin.qq.com/s/vJOE6E9okquyorf9JOe_Pw


    该文章在 2025/12/26 14:34:04 编辑过
    关键字查询
    相关文章
    正在查询...
    点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
    点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
    点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
    点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
    Copyright 2010-2026 ClickSun All Rights Reserved