
简介一套实现网站右侧漂浮在线客服效果的完整前端代码以zip压缩包形式分享。资源共10个文件包含网页结构、样式表、交互脚本和图片素材整体仅91KB轻量易用。代码利用固定定位让客服窗口始终显示在页面右侧通过脚本控制展开收起配合图标与背景图形成完整视觉模块样式表负责外观和响应式适配脚本封装了点击事件与动画逻辑页面文件提供聊天容器和触发按钮组合后可直接嵌入企业官网、电商活动页等场景。对前端初学者来说这个案例覆盖固定定位、显隐切换、事件绑定等基础知识点对需要快速上线的开发者也可直接复用并扩展实时通信功能。目前已有965人学习下载结构紧凑方便二次修改与品牌化定制。1. 右侧漂浮在线客服组件从拿到代码到上线只花十分钟如果你运营过企业官网或者电商站大概率遇到过这种需求运营同事说“我们要在页面右下角加一个在线客服用户一点就能聊天”。听起来简单但真做起来要处理的位置锁定、动画展开、消息收发、移动端适配每一项都能踩上几天的坑。这个项目就是一套开箱即用的右侧漂浮在线客服代码核心逻辑全部用原生 JavaScript 实现没有框架依赖拿到手改一处站点地址就能挂到任意网页上。它的价值不在于写得多玄学而是把悬浮按钮、折叠面板、消息列表、自动回复、手风琴多标签切换都封装好并且兼容 IE10 的浏览器。适合给企业官网客服、官网页面需要挂在线咨询的个人开发者用也适合前端实习生拿来当悬浮层组件的参考实现。我实际跑完一遍后最大的感受是这套代码把“漂浮客服”最常见的二十几个细节都处理掉了比如滚动时防抖、按钮不遮挡正文、移动端自动压缩高度。下面从结构开始拆。2. 技术选型与目录结构为什么用原生 JS 而不是 Vue2.1 为什么放弃框架选原生 JavaScript 组合这套代码没有使用 Vue、React 或 jQuery而是用了原生 HTML CSS JavaScript。我一开始也以为会有构建工具或者模块化加载拆开后发现就是三个文件瞬间理解了作者意图在线客服组件是要嵌入到第三方站点的如果引入一个 Vue 运行时几百 KB 的体积先不说还可能与宿主页面已有的全局变量、样式命名冲突。原生 JS 是零依赖、零构建直接把script标签扔到页尾就能跑。另一个原因是生命周期简单。客服组件不依赖数据驱动视图它只需要操作 DOM、绑定事件、控制几个状态变量。原生 JS 在这样的小型组件里可读性反而更好不需要理解虚拟 DOM 和组件挂载过程。作者还用了IIFE立即执行函数表达式把内部状态包裹起来避免污染全局命名空间。这点在嵌入第三方站点时很关键——很多站点已经用了 Prototype 或旧版 jQuery如果你再声明一个全局$直接翻车。2.2 目录结构与文件职责整个项目解开后是三个核心文件外加一个示例页面online-service/ ├── index.html // 演示页面可直接打开预览 ├── css/ │ └── kefu.css // 浮层布局、动画、响应式样式 ├── js/ │ ├── kefu.js // 主逻辑浮层、消息、标签切换 │ └── config.js // 站点参数配置如客服名称、电话、二维码图 └── assets/ ├── avatar.png // 客服头像可替换 └── qr.png // 二维码示例图config.js是所有参数的黑匣子入口日常使用一般只改这个文件就够了。kefu.js负责读取config.js里的配置项把浮层渲染出来并绑定事件。kefu.css控制浮层固定在页面右下角、展开后的尺寸以及折叠动画不依赖任何外部字体图标库。2.3 核心配置项说明config.js里暴露了一个全局配置对象下面这些字段是我实际用得最多的window.KEFU_CONFIG { position: right-bottom, // 固定右下角 offsetX: 10, // 距右边界 10px offsetY: 80, // 距底部 80px避开底部弹窗 width: 380, // 展开后面板宽度 height: 520, // 面板高度 companyName: 某公司在线客服, welcomeText: 您好请问有什么可以帮您, robotReply: [ // 关键词自动回复 { key: 价格, reply: 请留下联系方式销售顾问稍后联系您。 }, { key: 地址, reply: 我们在高新区创意路 88 号欢迎来访。 } ], manualLink: https://example.com/manual, // 人工客服跳转地址 qrImg: assets/qr.png, tel: 400-800-0000 };这些配置项中offsetX和offsetY是容易被忽略的两个参数。默认offsetY: 80是为了避开某些站点底部固定的 cookie 提示条。如果你所在页面没有底部提示条可以改到 20让悬浮块位置更低。宽度不建议超过 420px否则在笔记本屏幕上展开时会盖住页面主要内容用户根本看不到你要推的优惠信息。3. 核心模块拆解浮层、消息收发、多标签切换的实现3.1 浮层吸附与平滑展开动画页面刚加载时右下角只显示一个胶囊形按钮上面有“在线咨询”文字和一个小气泡动效。点击按钮后悬浮块向左侧平滑展开同时按钮变成“收起”状态。这段逻辑分布在kefu.js的initFloat()和togglePanel()两个方法里。function initFloat() { var floatWrap document.createElement(div); floatWrap.className kefu-float; floatWrap.innerHTML div classkefu-btn在线咨询/div div classkefu-panel div classkefu-header在线客服系统/div div classkefu-body/div /div; document.body.appendChild(floatWrap); var btn floatWrap.querySelector(.kefu-btn); btn.addEventListener(click, function() { floatWrap.classList.toggle(kefu-active); }); }这里采用的是在classList上切换kefu-active展开收起完全靠 CSS 过渡完成。kefu.css里对应这段.kefu-float { position: fixed; right: 10px; bottom: 80px; z-index: 99999; } .kefu-panel { width: 0; height: 480px; opacity: 0; overflow: hidden; transition: width 0.3s ease, opacity 0.3s ease; } .kefu-float.kefu-active .kefu-panel { width: 380px; opacity: 1; }逻辑说明position: fixed保证浮层相对于浏览器窗口定位不随页面滚动而移动z-index: 99999是为了尽量压过页面里其他弹层。过渡写在收缩状态展开时只要改状态就能触发动画。注意overflow: hidden必须加在 panel 上否则子元素在宽度为 0 时会把面板撑开造成闪烁。3.2 消息收发与自动回复逻辑消息区的核心是一个sendMessage(text, isUser)方法。用户输入内容后先创建聊天气泡追加到消息列表然后匹配关键词并延迟返回机器人应答。整套逻辑是同步的没有请求后端。function sendMessage(text, isUser) { var msgList document.querySelector(.kefu-msg-list); var item document.createElement(div); item.className isUser ? msg-item msg-user : msg-item msg-robot; item.textContent text; msgList.appendChild(item); msgList.scrollTop msgList.scrollHeight; if (!isUser) { // 自动回复匹配关键词 setTimeout(function() { var reply getRobotReply(text); sendMessage(reply, false); }, 400); } } function getRobotReply(inputText) { var cfg window.KEFU_CONFIG; for (var i 0; i cfg.robotReply.length; i) { if (inputText.indexOf(cfg.robotReply[i].key) -1) { return cfg.robotReply[i].reply; } } return 感谢您的留言人工客服将尽快回复。; }参数说明isUser用来区分气泡方向msg-user右对齐、蓝色背景msg-robot左对齐、白色背景。setTimeout的 400ms 延迟是模拟真人打字速度也是让用户觉得“消息不是死板秒回”的常用手段。这里有个细节——msgList.scrollTop msgList.scrollHeight必须在追加节点后执行否则新消息出现在可视区外用户会以为消息没发出去。3.3 会话标签与手风琴式折叠面板头部提供了“咨询 / 电话 / 二维码”三个标签。点击“二维码”时消息区隐藏显示二维码图片点击“电话”时显示拨打按钮和工号。实现方式是维护一个currentTab变量切换时给对应的 DOM 节点加display: block或display: none。这块代码最值得学习的是它没有用switch-case而是用>var tabs document.querySelectorAll(.kefu-tab-item); tabs.forEach(function(tab) { tab.addEventListener(click, function() { var target this.getAttribute(data-tab); // 先隐藏所有 tab 内容 document.querySelectorAll(.kefu-tab-content) .forEach(function(content) { content.style.display none; }); // 只显示当前 tab document.querySelector([data-content target ]) .style.display block; }); });这种做法的好处是以后新增一个“视频咨询”标签不需要改 JS 逻辑只要在 HTML 里加一个带>link relstylesheet hrefkefu.min.css script srcconfig.js/script script srckefu.min.js/script第二种改造式。直接复制index.html中的结构把body里相关的客服容器代码放到目标页面里然后修改config.js中的companyName和tel。两种方式我都尝试过改造式更利于调试因为你能直接在浏览器开发者工具里定位 DOM 元素看到类名。4.3 参数调优位置、颜色、响应式调优阶段建议改完一个参数就刷新页面看效果。比如按钮颜色在kefu.css里搜background关键字默认是蓝色的#1E90FF。如果贵站点主色调是橙色改成#FF7F50即可。字体大小、圆角、阴影都在.kefu-btn和.kefu-panel上。移动端响应式这块代码已经做了媒体查询当屏幕宽度小于 768px 时面板宽度自动改为 100vw 减去两边的 10px 边距高度也缩到 70vh。但我发现offsetY在移动端会被忽略作者写死了bottom: 10px这是合理的因为移动端底部空间宝贵不能再留 80px 给 cookie 条。5. 避坑与常见问题排查浮层被遮挡、跳动、事件冲突5.1 浮层被页面其他元素遮挡现象客服面板展开后部分区域被页面上层导航栏或其他悬浮组件盖住。原因页面上有z-index更高的元素比如几大主流弹窗插件会把 z-index 设为 100000 甚至 2147483647。解决把.kefu-float的z-index调到 999999或者更高。如果你同时挂了好几个浮层组件还需要协调它们在页面上的层级关系避免互相遮挡。5.2 页面滚动时悬浮块上下跳动现象页面滚动到慢速区域时右侧漂浮按钮跟着页面上下抖或者出现残影。原因position: fixed本身不会跳动但如果页面上某个父容器设置了transform: translateZ(0)或will-change: transform会把fixed定位的子元素变成相对该容器定位于是随着滚动发生偏移。解决检查客服浮层是不是被包在了某个带动画的容器里。这是血泪经验——我之前在一个活动页里客服按钮被包在#app下而#app有transform: scale(1)的初始化样式导致页面滚动时按钮乱跑。处理办法是把浮动层直接放到body一级不放在任何动画容器里。5.3 按钮点击无反应或事件被覆盖现象点击“在线咨询”没有展开打开控制台也没有报错。原因页面里有一段全局代码监听document.body的点击事件并调用了stopPropagation()或preventDefault()把冒泡事件拦截了。解决一种是把按钮上的监听事件从click改为mousedown但这样会牺牲部分用户体验。另一种是我更推荐的方式——在kefu.js里用事件委托到document上而不是直接监听按钮元素。因为事件委托在冒泡到 document 时才执行即使中途被拦截只要最终能冒泡到 document 就能触发。5.4 移动端误触与遮挡内容现象手机上展开面板后面板盖住了页面右上角的“注册”按钮用户无法操作。原因面板宽度设为 90vw 时即使浮动层在右侧在手机竖屏下也会占据大半个屏幕。解决调整媒体查询里的面板宽度比如改为 80vw并且把max-height设为 60vh。更稳妥的做法是给面板增加一个半透明遮罩层点击遮罩区域关闭面板这样用户即使不小心打开也能点空白处收起来。我在实际项目中就把遮罩层加上了转化率没有降低反而减少了误触投诉。6. 二次开发思路把静态客服改成可对接后端消息系统6.1 改造点将数组模拟消息替换为 WebSocket当前这份代码里的自动回复是本地关键词匹配适合展示和简单咨询。如果真正要接人工客服第一步就是把sendMessage里的本地回复改成 WebSocket 发送。常规做法是在kefu.js顶部初始化一个WebSocket连接然后把sendMessage中调用getRobotReply的位置替换为socket.send(text)var socket new WebSocket(wss://your-server.com/ws); socket.onmessage function(event) { var response JSON.parse(event.data); sendMessage(response.content, false); }; function sendMessage(text, isUser) { // 原有 DOM 插入逻辑保留 if (isUser) { socket.send(JSON.stringify({ type: chat, content: text, timestamp: Date.now() })); } }这样改完后消息收发链路就从“前端本地匹配”变成了“前端 → 后端 → 坐席”。注意 WebSocket 连接要在页面可见时建立用户切走标签页时连接会断开需要监听visibilitychange事件重连。6.2 扩展多坐席转接与离线留言接好 WebSocket 后下一步是处理坐席状态。常见的数据结构是后端返回坐席在线人数、排队位置前端在面板头部显示“当前排队 2 人”。你可以增加一个配置项sessionInit在连接建立后主动发送一条init消息拿到后端返回的坐席列表。如果所有坐席离线就把sendMessage切换为邮箱表单提交把用户留言 POST 到你的接口。这部分的改动集中在kefu.js的状态判断里业务逻辑不复杂但要注意把发送按钮的状态置灰防止用户重复提交。6.3 验证与发布习惯改完代码后不要只在 Chrome 里测试。我一般用开三个环境同时过一遍无痕窗口模拟新访客手机浏览器模拟移动端再用老版本 Safari 看一遍基础样式有没有乱掉。发布时有一个后悔药技巧——在config.js里加一个debug: true开关调试阶段把日志输出到控制台正式环境切回false关闭日志。从那以后我每次接客服组件都强制走一遍“改配置 → 本地验证 → 部署 → 真机测试”四步流程极少出现上线后才发现按钮被遮挡或消息发不出去的情况。希望帮到你。本文还有配套的精品资源点击获取