前端小白别慌:30分钟搞懂CSS sticky粘性布局(附实战代码)

前端小白别慌:30分钟搞懂CSS sticky粘性布局(附实战代码)
在这里插入图片描述


前端小白别慌:30分钟搞懂CSS sticky粘性布局(附实战代码)

前端小白别慌:30分钟搞懂CSS sticky粘性布局(附实战代码)

说实话,我刚入行那会儿,第一次看到position: sticky这玩意儿,脑子里只有一个念头:这啥鬼东西?写了个position: sticky上去,心想这下导航栏应该能吸顶了吧,结果一滚动,它跟着页面一起跑了,跑得比我还快。我当时就怀疑人生了,说好的"粘性"呢?粘在哪儿了?胶水过期了吗?

后来我才明白,这玩意儿的坑,比我想象的深得多。它不是不好用,是你得懂它的脾气。今天咱就不整那些官方文档的八股文,我就把我这些年踩过的坑、流过的泪,还有那几个亮瞎眼的实战案例,一股脑儿全倒给你。保证你看完,sticky这玩意儿在你面前就是个小猫咪,不再是那个让你加班到半夜的拦路虎。

sticky到底是个啥混血儿?

咱们先把这个概念掰扯清楚。很多教程一上来就说"sticky是相对定位和固定定位的混合",然后就没了。这说了等于没说,我跟你说个接地气的比喻:

如果你的HTML是骨架,CSS是衣服,那position: fixed就是那种死板板的塑身衣,一旦穿上,它就死死钉在屏幕的某个位置,雷打不动,滚动条都挡不住它的倔强。而position: relative就像是你的普通T恤,你让它往左挪点它就挪点,但滚动的时候它该走还是走。

position: sticky呢?它是个智能外套,平时穿着跟普通衣服一样该干嘛干嘛(表现得像relative),但当你快要滚出屏幕的时候,突然"啪"的一下贴在你身上了(变成fixed),等你滚回去的时候,它又乖乖恢复原状。

说白了,它是个"看心情"的定位方式。但这个"看心情"是有条件的,它得看你有没有给它设置"阈值"。

thresholds(阈值)不写,sticky就罢工

这是最基础的,但也是最要命的。很多人(包括当年的我)以为写了position: sticky就完事了,结果发现屁用没有。为啥?因为你没告诉它"什么时候该粘住"。

这个阈值就是toprightbottomleft这四个属性。对于垂直滚动的页面,我们最常用的是top

来看看这个最基础的例子,感受一下什么叫"无top,不粘滞":

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Sticky基础演示-错误示范</title><style>body{margin: 0;font-family: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, sans-serif;height: 200vh;/* 让页面足够长,能滚动 */background:linear-gradient(to bottom, #f0f0f0, #e0e0e0);}.header{height: 200px;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);display: flex;align-items: center;justify-content: center;color: white;font-size: 2rem;}.sticky-nav-wrong{/* 卧槽,我写了sticky,咋不干活? */position: sticky;/* 看到了吗?我没写top! */background: #ff6b6b;padding: 20px;color: white;text-align: center;font-size: 1.2rem;}.content{padding: 40px;max-width: 800px;margin: 0 auto;line-height: 1.8;}.content p{margin-bottom: 20px;font-size: 1.1rem;color: #444;}</style></head><body><divclass="header"> 头部Banner区域 </div><!-- 这个导航栏写了sticky但是不顶用 --><navclass="sticky-nav-wrong"> 我写了position: sticky但没有top,所以我就是个摆设 </nav><divclass="content"><p>往下滚...往下滚...你会发现上面那个红条根本不粘,它就跟着页面一起跑了。</p><p>这就是小白最容易踩的第一个坑。</p><!-- 重复生成一些段落撑开高度 --><p>开发这条路,坑多着呢...</p><p>继续滚,你看我说的是不是对的...</p></div></body></html>

看到没?上面那个红色的导航栏,你滚动手试试,它根本不会粘在顶部。现在咱们改成正确的版本,就加一行top: 0,奇迹就会发生:

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Sticky基础演示-正确示范</title><style>body{margin: 0;font-family: system-ui, -apple-system, sans-serif;height: 300vh;background:linear-gradient(to bottom, #1a1a2e, #16213e);}.hero{height: 300px;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);display: flex;align-items: center;justify-content: center;color: white;font-size: 3rem;text-shadow: 2px 2px 4px rgba(0,0,0,0.3);}.smart-nav{position: sticky;/* 关键代码来了! */top: 0;/* 当元素顶部距离视口顶部0px的时候,给我粘住! */background:rgba(255, 107, 107, 0.95);padding: 20px;color: white;text-align: center;font-size: 1.2rem;box-shadow: 0 2px 10px rgba(0,0,0,0.1);/* 加个z-index防止被盖住 */z-index: 100;backdrop-filter:blur(10px);/* 毛玻璃效果,现代浏览器支持 */}.main-content{padding: 40px;max-width: 800px;margin: 0 auto;color: #eee;line-height: 1.8;}.card{background:rgba(255,255,255,0.1);padding: 30px;margin-bottom: 30px;border-radius: 12px;border: 1px solid rgba(255,255,255,0.2);}</style></head><body><divclass="hero"> 炫酷的头部区域 </div><navclass="smart-nav"> 智能粘性导航栏 - 注意观察我什么时候"粘"住! </nav><divclass="main-content"><divclass="card"><h2>看到了吗?</h2><p>当你滚动到导航栏触及视口顶部的时候,它就会"啪"的一下粘在那里。</p><p>而当你在顶部,还没滚动到它的时候,它是正常流式布局的一部分。</p></div><!-- 生成一堆内容方便测试 --><divclass="card"><p>继续滚动测试...</p></div><divclass="card"><p>粘性定位真的很神奇...</p></div><divclass="card"><p>不像fixed那样一直飘在屏幕上...</p></div><divclass="card"><p>它有"生命周期"的...</p></div></div></body></html>

看到了吧?top: 0就像是给sticky元素设定的"警戒线"。当元素在滚动过程中,它的顶部碰到这个警戒线(距离视口顶部0像素)的时候,它就会变成固定定位。当你往回滚,滚过了它原本应该在的位置,它又会恢复成相对定位。

这里有个细节你可能没注意:sticky元素在文档流中是占位的!这跟fixed完全不同。fixed是脱标的,不占空间,所以当你把一个元素变成fixed,下面的内容会突然往上跳一下,那种体验要多难受有多难受。但sticky不会,它一直占着那个坑,只是视觉上"粘"住了。这就是为啥我说它是现代网页的优雅解法。

那些年被父容器overflow坑哭的夜

好了,基础概念懂了,想信心满满去实战了?且慢!着我跟你说,sticky最大的坑,不是语法,而是它爸妈不给力

啥意思呢?sticky元素的粘性效果,只能在它的直接父容器或祖先容器没有设置overflow: hiddenoverflow: scrolloverflow: auto的情况下才能生效。一旦父容器设置了这些属性,sticky当场失效,毫无商量余地。

这个坑我踩得那叫一个深。有一次做后台管理系统,我想让左侧菜单sticky粘性定位,结果死活不生效。我检查了top写了,position写了,z-index也加了,甚至怀疑人生到重启电脑。最后才发现,父容器为了清除浮动,加了个overflow: hidden。卧槽,当时真的想砸键盘。

来,看这个血泪案例:

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Sticky的噩梦:overflow:hidden</title><style>body{margin: 0;font-family: system-ui;height: 300vh;background: #0f0f23;color: #fff;}.wrapper{/* 罪魁祸首在这里! */overflow: hidden;/* 就这一行,把sticky的腿给打断了 *//* 或者是 overflow: auto; *//* 或者是 overflow: scroll; *//* 反正只要有overflow非visible,sticky就凉 */width: 90%;margin: 0 auto;background:rgba(255,255,255,0.05);padding: 20px;}.sidebar{position: sticky;top: 20px;/* 距离顶部20px时粘住 */width: 200px;float: left;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);padding: 20px;border-radius: 8px;color: white;}.main{margin-left: 240px;padding: 20px;min-height: 150vh;background:rgba(255,255,255,0.02);line-height: 2;}.warning-box{background: #ff4757;padding: 20px;margin-bottom: 20px;border-radius: 8px;text-align: center;}</style></head><body><divclass="warning-box"> 注意:这个wrapper设置了overflow: hidden,sticky会失效! </div><divclass="wrapper"><asideclass="sidebar"><h3>我是侧边栏</h3><p>理论上我应该粘在顶部</p><p>但因为父容器有overflow,我粘不住了...</p><p>😭 救命啊</p></aside><mainclass="main"><h1>内容区域</h1><p>往下滚动试试看...</p><p>你会发现左侧的侧边栏根本不愿意粘住,它只想跟着页面自由飞翔。</p><p>这就是sticky最大的坑,没有之一。</p><!-- 填充内容 --><p>Lorem ipsum dolor sit amet...</p><p>继续填充...</p><p>再填充...</p></main></div></body></html>

怎么解决?要么把sticky元素移出那个设置了overflow的容器,要么就不要用overflow来清除浮动(改用伪元素clearfix,或者用display: flow-root)。这个坑真的害人不浅,你现在知道了,以后能少熬三个小时的夜。

移动端适配时sticky突然"失忆"的真相

做移动端的时候,sticky还有个隐形坑。有时候你在Chrome DevTools里模拟移动端,sticky工作得好好的,但一到真机上,特别是iOS的Safari或者某些安卓的WebView里,它就"失忆"了,不粘了。

这多半是容器高度的问题。sticky要生效,它所在的容器必须有足够的高度,或者说,它必须在一个是滚动上下文的容器里。

还有一种情况是,移动端有时候会给body或者html设置height: 100%或者overflow: hidden来实现某些全屏效果,这就间接导致了sticky失效。

来看个移动端专用的修复方案:

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>移动端Sticky完美适配方案</title><style>*{margin: 0;padding: 0;box-sizing: border-box;}html, body{/* 移动端有时候会设height: 100%,这会导致sticky出问题的 *//* 解决方案:min-height代替height */min-height: 100vh;font-family: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto,"Helvetica Neue", Arial, sans-serif;background: #f5f5f5;}/* 滚动容器单独设置,不要直接给body加奇怪的overflow */.scroll-container{overflow-y: auto;/* 如果需要局部滚动,单独设在这里 */-webkit-overflow-scrolling: touch;/* iOS惯性滚动必备 */}.article-header{background: #2c3e50;color: white;padding: 40px 20px;text-align: center;}.sticky-toc{position: sticky;top: 0;background: white;padding: 15px;box-shadow: 0 2px 5px rgba(0,0,0,0.1);z-index: 10;/* iOS Safari兼容修复 */position: -webkit-sticky;/* 旧版Safari前缀,虽然现在基本不需要了,但加上保险 */}.toc-list{display: flex;overflow-x: auto;gap: 20px;list-style: none;padding: 0;margin: 0;/* 隐藏滚动条但保留功能 */scrollbar-width: none;/* Firefox */-ms-overflow-style: none;/* IE 10+ */}.toc-list::-webkit-scrollbar{display: none;/* Chrome Safari */}.toc-list li{white-space: nowrap;color: #666;font-size: 14px;}.article-body{padding: 20px;line-height: 1.8;font-size: 16px;color: #333;}.article-body p{margin-bottom: 20px;}.article-body h2{margin: 30px 0 15px;color: #2c3e50;}/* iOS底部安全区域适配 */@supports(padding-bottom:env(safe-area-inset-bottom)){.article-body{padding-bottom:env(safe-area-inset-bottom);}}/* 修复iOS sticky不稳定的hack */@supports(-webkit-touch-callout: none){.sticky-toc{/* 针对iOS设备的额外样式 */transform:translateZ(0);/* 开启硬件加速有时候能修复奇怪的问题,但注意transform和sticky的关系! */will-change: transform;/* 提示浏览器优化 */}}</style></head><body><divclass="scroll-container"><headerclass="article-header"><h1>移动端Sticky实战</h1><p>解决iOS Safari和安卓WebView的兼容问题</p></header><navclass="sticky-toc"><ulclass="toc-list"><li>第一章:基本功</li><li>第二章:踩坑实录</li><li>第三章:性能优化</li><li>第四章:高级玩法</li><li>第五章:总结</li></ul></nav><articleclass="article-body"><h2>序章:移动端的水很深</h2><p>在移动端使用sticky,你要考虑的东西比PC端多得多。-webkit-sticky前缀要考虑吧?安全区域要适配吧?惯性滚动要开启吧?</p><h2>第一章:基本功</h2><p>sticky在移动端的语法和桌面端一样,但要注意父容器的高度问题。如果父容器高度没有内容高,sticky是不会生效的...</p><!-- 大量内容填充 --><p>继续往下滚动测试粘性效果...</p><p>在iPhone上测试时,注意观察地址栏收起时的表现...</p><p>有时候地址栏的显示/隐藏会影响视口计算...</p><h2>第二章:踩坑实录</h2><p>overflow: hidden的坑我们已经说过了。但在移动端,还有一个坑是输入框弹出时的键盘...</p><!-- 重复内容以便测试滚动 --><p>内容填充...</p><p>内容填充...</p><p>当你滚到这里的时候,顶部的目录应该还粘在那里...</p></article></div></body></html>

这里我要特别提醒一下那个@supports (-webkit-touch-callout: none),这个是检测iOS设备的CSS Hack。有时候iOS上的sticky会有奇怪的闪烁问题,开启硬件加速(transform: translateZ(0))能缓解,但要注意我后面要说的transform和sticky的"爱情悲剧"。

浏览器兼容性:Safari说它行,IE说它不存在

说到兼容性,sticky这货在现代浏览器(Chrome、Firefox、Safari、Edge)上表现都还不错,但你要是遇到需要支持IE的项目… 别想了,IE不认识sticky,它会觉得你在写天书。

好消息是,现在绝大多数项目都不用管IE了。坏消息是,Safari虽然支持sticky,但某些老版本(iOS 12及以下)可能会有奇怪的bug,比如计算位置不准,或者和其他CSS属性配合时出问题。

稳妥的做法是做个兜底:

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>Sticky兼容性处理</title><style>.navbar{/* 渐进增强策略:先写普通的fixed/relative作为fallback */position: relative;background: #333;padding: 15px;color: white;}/* 浏览器支持sticky的话,覆盖上面的样式 */@supports(position: sticky)or(position: -webkit-sticky){.navbar{position: -webkit-sticky;/* Safari旧版本 */position: sticky;top: 0;z-index: 1000;}}/* 如果一定要支持IE,可以用JS检测然后加class,或者用fixed做降级 */.is-ie .navbar{position: fixed;top: 0;left: 0;right: 0;/* 但要记得给body加padding-top,否则内容会被遮住 */}</style></head><body><!-- IE条件注释(虽然现在用得少了,但知道一下没坏处) --><!--[if IE]> <script> document.body.classList.add('is-ie'); </script> <![endif]--><navclass="navbar"> 兼容性处理的导航栏 </nav><divstyle="padding-top: 60px;"> IE下这里要加padding-top,因为navbar变成fixed脱标了 </div></body></html>

不过我听说现在还有公司在支持IE11,如果你不幸在那样的公司… 要不考虑换个工作?开玩笑的,你可以用Polyfill,或者干脆用JS监听scroll事件来模拟sticky效果。但JS模拟的性能肯定不如原生的sticky,特别在移动端可能会卡顿。

调试小妙招:亮瞎眼的背景色大法

调试sticky最痛苦的是什么?是肉眼根本看不出它什么时候从relative变成fixed了!它粘住的时候你很难察觉到边界在哪里,特别是当元素刚好在边界上抖动的时候。

我有个独家秘籍,堪称debug界的核武器:给sticky元素加个亮瞎眼的背景色,再加个过渡动画

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>Sticky调试技巧</title><style>body{margin: 0;height: 200vh;background: #f0f0f0;font-family: system-ui;}.debug-sticky{position: sticky;top: 0;padding: 20px;color: white;text-align: center;font-weight: bold;/* 调试模式下的核武器样式 */background: red !important;/* 亮瞎眼的红色 */outline: 5px solid yellow !important;/* 黄色边框 */outline-offset: -5px;/* 让它粘住的时候有变化 */transition: all 0.3s ease;box-shadow: none;}/* 这个技巧神了:当元素是sticky状态时(也就是滚动到top时), 我们可以用类似于min/max的技巧或者用JS监听, 但更简单的是直接观察它是否在视口顶部 */.debug-marker{position: fixed;top: 0;left: 0;right: 0;height: 2px;background: lime;/* 荧光绿警戒线 */z-index: 9999;pointer-events: none;}/* 实际开发中,你可以这样观察sticky元素的行为 */.sticky-dev-mode{position: sticky;top: 0;background: #333;padding: 20px;color: #fff;/* 调试辅助线 */border-bottom: 3px solid transparent;}/* 用Intersection Observer API检测元素的粘性状态(需要JS) */.is-sticking{background: #ff6b6b !important;/* 粘住时变红 */border-bottom-color: #ffff00;}</style></head><body><!-- 调试辅助线 --><divclass="debug-marker"></div><divstyle="height: 100px;background: white;padding: 20px;"><h1>滚动页面观察下方的sticky元素</h1><p>注意看那条荧光绿的线,当元素碰到那条线的时候,它就会变成粘性状态</p></div><divclass="debug-sticky"id="testSubject"> 我是被调试的对象 - 注意观察我的背景色和边框! <br>滚动到顶部时看这个元素的行为 </div><divstyle="padding: 20px;max-width: 800px;margin: 0 auto;"><p>开始滚动...</p><p>继续滚动...</p><p>再滚动一点,注意观察上方元素什么时候"粘"住...</p><!-- 大量内容 --><script>// 自动生成内容for(let i =0; i <50; i++){ document.write(`<p>第${i+1}段填充内容,用于测试滚动...</p>`);}</script></div><script>// 进阶调试:检测元素是否处于sticky状态const stickyElement = document.getElementById('testSubject');const observer =newIntersectionObserver((entries)=>{ entries.forEach(entry=>{if(!entry.isIntersecting){// 当元素不完全在视口内时(对于top:0的sticky,通常意味着已经粘住了) console.log('元素可能处于sticky状态'); stickyElement.classList.add('is-sticking');}elseif(entry.intersectionRect.top === entry.boundingClientRect.top){ stickyElement.classList.remove('is-sticking');}});},{threshold:[0,1]}); observer.observe(stickyElement);</script></body></html>

看到那根荧光绿的线了吗?那代表的是top: 0的位置。当你的sticky元素顶部碰到这根线的时候,它就会激活粘性状态。这招比你在Chrome DevTools里翻来覆去找状态管用多了。

还有个更简单粗暴的方法:在Elements面板里,直接把sticky元素的position手动改成static,看看页面会不会发生变化。如果没变化,说明它本来就没粘住;如果页面布局突然变了(往下掉了一块),说明之前它确实是sticky状态在占位。

性能警告:长列表里狂塞stickty,等待你的只有PPT

前面说了这么多怎么用,现在得说说什么情况下别用

如果你在一个长列表(虚拟列表或者无限滚动那种)里,给每一行或者每隔几行就加一个sticky,那性能会炸裂。为啥?因为sticky元素在滚动过程中,浏览器需要不断计算它是否越过了阈值,这比普通的static元素要消耗更多的渲染资源。

想象一下,你有1000个sticky元素,用户疯狂滚动,浏览器要实时计算1000个元素的位置关系… 那画面太美我不敢看。

正确的做法是,只在必要的地方用sticky,比如表头、侧边栏主目录。如果你需要那种"堆叠卡片"的效果(就是一张一张往上叠的那种),也别真给每个卡片都加sticky,可以用一些优化手段。

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>Sticky性能优化 - 长列表慎用</title><style>body{margin: 0;font-family: system-ui;}/* 错误示范:每个项目都sticky,性能灾难 */.bad-performance li{position: sticky;top: 0;/* 1000个sticky项目,滚动起来卡成狗 */background: white;/* ... */}/* 正确做法1:使用contain限制影响范围 */.optimized-container{/* contain属性告诉浏览器:这个元素内部的布局变化不要影响到外部 */contain: layout style paint;/* 或者更强力的 */contain: strict;}.sticky-header{position: sticky;top: 0;background: #2c3e50;color: white;padding: 15px;z-index: 10;}/* 正确做法2:对于超大数据,使用虚拟滚动配合固定表头 */.virtual-list-container{height: 600px;overflow: auto;position: relative;}.virtual-header{position: sticky;top: 0;background: #e74c3c;color: white;padding: 10px;z-index: 20;/* will-change是双刃剑,能提升滚动性能但会增加内存占用 */will-change: transform;}.list-item{padding: 15px;border-bottom: 1px solid #eee;height: 50px;/* 固定高度对虚拟滚动很重要 */}/* 使用CSS Containment优化 */.item-row{contain: layout;}</style></head><body><h1>长列表Sticky性能优化示例</h1><divclass="optimized-container"><divclass="sticky-header"> 表格表头(只有这一个sticky,不要每行都stick) </div><divclass="item-row"><divclass="list-item">数据行 1 - 使用contain优化</div></div><divclass="item-row"><divclass="list-item">数据行 2</div></div><!-- 假设这里有1000行 --></div><script>// 动态生成内容const container = document.querySelector('.optimized-container');for(let i =3; i <=100; i++){const row = document.createElement('div'); row.className ='item-row'; row.innerHTML =`<div>数据行 ${i} - 注意CSS contain属性的作用</div>`; container.appendChild(row);}</script></body></html>

这里我提到了一个高级属性contain: layout style paint,这个是CSS Containment规范的内容。它就像是给浏览器吃了一颗定心丸:“兄弟,我这个元素里面的变化你别管,只管渲染它自己就行了,别去做全局重排重绘。”

对于sticky元素,特别是那种在复杂布局里的,加上contain能显著提升滚动性能。但要注意兼容性,IE肯定不支持,老版本Chrome也不支持。

还有一个神器是content-visibility: auto,这个更猛,直接让视口外的元素不渲染,配合contain-intrinsic-size使用,能让长列表的Sticky性能提升几十倍。不过这个属于超纲内容,先记住有这东西,下次专门开一篇讲。

实战:Sticky做侧边目录、表头固定、悬浮按钮

好了,理论说了一堆,来点实战的。这三个场景是我工作中最常用的sticky案例。

场景一:智能侧边目录(类似掘金、知乎的右侧目录)

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Sticky侧边目录实战</title><style>*{margin: 0;padding: 0;box-sizing: border-box;}body{font-family: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto,"Helvetica Neue", Arial, sans-serif;background: #f4f5f5;line-height: 1.6;color: #333;}.layout{max-width: 1200px;margin: 0 auto;padding: 20px;display: flex;gap: 30px;}.main-content{flex: 1;background: white;padding: 40px;border-radius: 4px;box-shadow: 0 1px 2px rgba(0,0,0,0.1);}.article-title{font-size: 32px;font-weight: 700;margin-bottom: 20px;color: #252933;}.article-meta{color: #8a919f;font-size: 14px;margin-bottom: 30px;padding-bottom: 20px;border-bottom: 1px solid #e5e6eb;}.article-body h2{font-size: 24px;margin: 40px 0 20px;color: #252933;font-weight: 600;}.article-body p{margin-bottom: 20px;font-size: 16px;line-height: 1.8;color: #515767;}/* 侧边栏布局 */.sidebar{width: 300px;flex-shrink: 0;}.toc-container{/* 关键:粘性定位 */position: sticky;top: 80px;/* 距离视口顶部80px,给顶部导航留空间 */background: white;border-radius: 4px;box-shadow: 0 1px 2px rgba(0,0,0,0.1);padding: 20px;max-height:calc(100vh - 100px);/* 限制最大高度,防止超长目录 */overflow-y: auto;/* 目录太长时可以内部滚动 */}.toc-title{font-size: 16px;font-weight: 600;color: #252933;margin-bottom: 15px;padding-bottom: 10px;border-bottom: 1px solid #e5e6eb;}.toc-list{list-style: none;position: relative;}/* 左边加个小竖线装饰 */.toc-list::before{content:'';position: absolute;left: 0;top: 8px;bottom: 8px;width: 2px;background: #e5e6eb;}.toc-list li{margin-bottom: 12px;padding-left: 16px;position: relative;font-size: 14px;}/* 当前章节的指示点 */.toc-list li.active::before{content:'';position: absolute;left: -1px;top: 50%;transform:translateY(-50%);width: 4px;height: 16px;background: #1e80ff;border-radius: 2px;}.toc-list li.active a{color: #1e80ff;font-weight: 500;}.toc-list a{color: #515767;text-decoration: none;display: block;transition: color 0.2s;/* 文字截断 */overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.toc-list a:hover{color: #1e80ff;}.toc-list .toc-h3{padding-left: 24px;font-size: 13px;}/* 作者卡片也sticky,但粘在目录下面 */.author-card{position: sticky;top: 400px;/* 在目录下方,形成接力效果 */margin-top: 20px;background: white;padding: 20px;border-radius: 4px;box-shadow: 0 1px 2px rgba(0,0,0,0.1);display: flex;align-items: center;gap: 12px;}.author-avatar{width: 48px;height: 48px;border-radius: 50%;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);}.author-info h4{font-size: 15px;color: #252933;margin-bottom: 4px;}.author-info p{font-size: 13px;color: #8a919f;}/* 响应式:移动端隐藏侧边栏 */@media(max-width: 768px){.sidebar{display: none;}.main-content{padding: 20px;}}</style></head><body><divclass="layout"><articleclass="main-content"><h1class="article-title">深入理解CSS Sticky定位:从入门到实战</h1><divclass="article-meta">2024-01-15 · 阅读 8888 · 点赞 666</div><divclass="article-body"><p>在前端开发中,布局一直是个让人头疼的话题。...</p><h2>第一章:什么是Sticky定位</h2><p>Sticky定位是CSS3中新增的一个定位属性...</p><p>更多内容...</p><h2>第二章:Sticky与Fixed的区别</h2><p>很多人容易把sticky和fixed搞混...</p><p>继续展开...</p><h2>第三章:实战应用场景</h2><p>在实际项目中,sticky最常用的场景有...</p><!-- 生成大量内容以便测试滚动 --><script>for(let i =0; i <30; i++){ document.write(`<p>这里是更多的文章内容用于填充篇幅,测试sticky效果。第${i+1}段内容...</p>`);}</script></div></article><asideclass="sidebar"><divclass="toc-container"><divclass="toc-title">目录</div><ulclass="toc-list"><liclass="active"><ahref="#ch1">第一章:什么是Sticky定位</a></li><liclass="toc-h3"><ahref="#ch1-1">1.1 基本概念</a></li><liclass="toc-h3"><ahref="#ch1-2">1.2 语法详解</a></li><li><ahref="#ch2">第二章:Sticky与Fixed的区别</a></li><li><ahref="#ch3">第三章:实战应用场景</a></li><liclass="toc-h3"><ahref="#ch3-1">3.1 导航栏吸顶</a></li><liclass="toc-h3"><ahref="#ch3-2">3.2 侧边目录</a></li><liclass="toc-h3"><ahref="#ch3-3">3.3 表格表头</a></li></ul></div><!-- 作者卡片也sticky,但粘在更下面的位置 --><divclass="author-card"><divclass="author-avatar"></div><divclass="author-info"><h4>前端老司机</h4><p>掘金签约作者 | 专注前端工程化</p></div></div></aside></div></body></html>

看看这个效果,目录粘在top: 80px的位置,作者卡片粘在top: 400px的位置。当你滚动到下方的时候,目录一直跟着你,方便你随时跳转。这就是掘金、知乎那种技术博客的标配布局。

注意我设置max-height: calc(100vh - 100px)overflow-y: auto这个小技巧,防止目录太长的时候用户够不着下面的章节。

场景二:表格表头固定(比传统方案优雅100倍)

做后台管理系统的时候,表格数据一大堆,滚动的时候表头不见了,用户就不知道这一列是啥意思了。以前我们怎么做?要么用两个table拼接,麻烦得要死;要么用JS计算高度,性能差得一批。现在有了sticky,一行代码搞定。

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>Sticky表格表头</title><style>body{margin: 0;padding: 20px;font-family: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, sans-serif;background: #f5f6fa;}.table-container{background: white;border-radius: 8px;box-shadow: 0 2px 8px rgba(0,0,0,0.1);overflow: auto;/* 表格可以横向滚动 */max-height: 70vh;/* 限制表格高度,超出滚动 */}table{width: 100%;border-collapse: separate;/* 必须是separate,不能是collapse,否则sticky可能失效 */border-spacing: 0;min-width: 1200px;/* 强制表格宽度,测试横向滚动 */}thead{/* 表头整体sticky(某些浏览器支持程度不一) */}th{/* 关键点:给每个th单独加sticky比给thead加兼容性更好 */position: sticky;top: 0;/* 粘在容器顶部 */background: #f8f9fa;z-index: 10;/* 表头在最上层,如果有左侧固定列需要更高z-index */z-index: 20;padding: 16px;text-align: left;font-weight: 600;color: #2c3e50;border-bottom: 2px solid #e9ecef;/* 和表格边框融合的小技巧 */box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1);}/* 如果还需要左侧第一列固定(常见于复杂数据表格) */th:first-child, td:first-child{position: sticky;left: 0;/* 横向粘住 */background: white;z-index: 11;/* 比普通th低,比tbody td高 */}/* 左上角那个单元格,既要上粘又要左粘,z-index要最高 */th:first-child{z-index: 30;background: #f8f9fa;/* 表头背景色 */}td{padding: 16px;border-bottom: 1px solid #e9ecef;color: #495057;background: white;/* 必须设置背景,否则滚动时会透出后面的内容 */}tr:hover td{background: #f8f9fa;}tr:hover td:first-child{background: #f8f9fa;/* 左侧固定列也要跟着变色 */}.status{display: inline-block;padding: 4px 12px;border-radius: 12px;font-size: 12px;font-weight: 500;}.status-active{background: #d4edda;color: #155724;}.status-pending{background: #fff3cd;color: #856404;}.status-inactive{background: #f8d7da;color: #721c24;}</style></head><body><h2>Sticky表格表头 + 首列固定</h2><p>向下滚动看表头粘顶,向右滚动看首列粘左</p><divclass="table-container"><table><thead><tr><th>用户ID</th><th>用户名</th><th>邮箱地址</th><th>部门</th><th>职位</th><th>状态</th><th>创建时间</th><th>最近登录</th><th>操作</th></tr></thead><tbody><!-- 动态生成数据 --><script>const statuses =['active','pending','inactive'];const statusText ={active:'在职',pending:'试用期',inactive:'离职'};const depts =['技术部','产品部','设计部','运营部','市场部'];for(let i =1; i <=30; i++){const status = statuses[Math.floor(Math.random()* statuses.length)]; document.write(` <tr> <td>#${String(i).padStart(4,'0')}</td> <td>用户${i}</td> <td>user${i}@company.com</td> <td>${depts[Math.floor(Math.random()* depts.length)]}</td> <td>${['高级工程师','产品经理','设计师','运营专员'][Math.floor(Math.random()*4)]}</td> <td><spantoken interpolation">${status}">${statusText[status]}</span></td> <td>2024-01-${String(Math.floor(Math.random()*28)+1).padStart(2,'0')}</td> <td>2024-01-29 10:00</td> <td><button>编辑</button></td> </tr> `);}</script></tbody></table></div></body></html>

这个例子我加了点料的:不仅有纵向的表头吸顶(top: 0),还有横向的首列固定(left: 0)。你会发现左上角的表头单元格(th:first-child)z-index特别高,因为它既要顶住上面的滚动,又要顶住左边的滚动,它处在"十字路口",层级必须是最高的。

注意我把表格的border-collapse设成了separate而不是默认的collapse。这是个深坑!如果设为collapse,浏览器在计算sticky元素的边框时会有bug,有时候粘不住,或者边框消失。虽然separate模式下的边框处理起来稍微麻烦点(看我用box-shadow模拟的下边框),但为了sticky能稳定工作,这是值得的。

场景三:悬浮按钮组(那种跟着页面走但又不碍事的操作栏)

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>Sticky悬浮操作栏</title><style>body{margin: 0;font-family: system-ui;background: #f0f2f5;}.page-header{height: 200px;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);color: white;display: flex;align-items: center;justify-content: center;font-size: 2rem;}.content-wrapper{max-width: 800px;margin: 0 auto;padding: 30px;position: relative;}/* 文章工具栏:粘在右侧,平时不显眼,滚动时固定在顺手位置 */.article-tools{position: sticky;float: right;top: 100px;/* 距离顶部100px,避开导航 */width: 60px;margin-right: -80px;/* 突出到主内容区右侧 */background: white;border-radius: 30px;box-shadow: 0 4px 12px rgba(0,0,0,0.15);padding: 10px 0;display: flex;flex-direction: column;align-items: center;gap: 10px;z-index: 100;}.tool-btn{width: 40px;height: 40px;border-radius: 50%;border: none;background: transparent;cursor: pointer;display: flex;align-items: center;justify-content: center;transition: all 0.2s;color: #666;font-size: 20px;}.tool-btn:hover{background: #f0f0f0;color: #333;transform:scale(1.1);}.tool-btn.active{color: #ff6b6b;}/* 评论输入框粘在底部 */.comment-input-bar{position: sticky;bottom: 0;/* 粘在底部! */background: white;padding: 15px 20px;border-top: 1px solid #e0e0e0;box-shadow: 0 -2px 10px rgba(0,0,0,0.05);margin: 0 -30px;/* 撑满容器宽度 */display: flex;gap: 12px;align-items: center;}.comment-input{flex: 1;border: 1px solid #e0e0e0;border-radius: 20px;padding: 10px 20px;outline: none;font-size: 14px;}.comment-input:focus{border-color: #667eea;}.send-btn{background: #667eea;color: white;border: none;padding: 10px 24px;border-radius: 20px;cursor: pointer;font-weight: 500;}.article-content{background: white;padding: 40px;border-radius: 8px;line-height: 1.8;font-size: 16px;color: #333;}.article-content p{margin-bottom: 20px;}/* 底部信息栏,让comment bar有地方可粘 */.footer-spacer{height: 100px;/* 给底部sticky留空间 */}@media(max-width: 1000px){.article-tools{/* 移动端改成底部工具栏 */position: fixed;bottom: 0;left: 0;right: 0;top: auto;width: 100%;margin: 0;flex-direction: row;justify-content: space-around;border-radius: 0;padding: 10px;border-top: 1px solid #e0e0e0;}}</style></head><body><divclass="page-header"> Sticky悬浮按钮实战 </div><divclass="content-wrapper"><!-- 右侧悬浮工具栏 --><divclass="article-tools"><buttonclass="tool-btn"title="点赞">♥</button><buttonclass="tool-btn"title="收藏">★</button><buttonclass="tool-btn"title="分享">⤴</button><buttonclass="tool-btn"title="评论">💬</button></div><articleclass="article-content"><h1>文章标题:Sticky的妙用</h1><p>在移动端,我们经常需要这种悬浮操作按钮...</p><!-- 生成内容 --><script>for(let i =0; i <40; i++){ document.write(`<p>这里是文章正文第${i+1}段,用于测试右侧工具栏的粘性效果以及底部的评论输入框...</p>`);}</script></article><divclass="footer-spacer"></div><!-- 底部粘性评论输入框 --><divclass="comment-input-bar"><inputtype="text"class="comment-input"placeholder="写下你的评论..."><buttonclass="send-btn">发送</button></div></div></body></html>

这个例子展示了bottom: 0的用法。看到那个评论输入框了吗?它不是一直在底部(fixed),而是当页面滚动到它所在的位置时,它才会粘在底部。如果你还在文章开头,根本看不到它,不像fixed那样阴魂不散一直挡住内容。

右侧那个心形点赞按钮组也可以用sticky做,比fixed的好处是,如果文章内容很短,不需要滚动,它就在它该在的位置,不会莫名其妙飘在屏幕中间。

惊天大坑:sticky遇上transform,爱情悲剧上演了

好了,前面都是铺垫,现在说这个能让你debug到秃头的坑:transform会创建新的层叠上下文,会把sticky给"困"住

要知道,sticky元素的粘性范围是受限于它的包含块(containing block)的。正常情况下,这个包含块就是最近的拥有滚动机制的祖先元素。但如果这个祖先元素设置了transform(除了none)、filter(除了none)、perspective(除了none)等属性,它就会创建一个新的包含块,sticky元素就会被困在这个transform-created containing block里,出不去了!

就像你给sticky元素套了个透明玻璃罩子,它只能在罩子里粘,出不了罩子。

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>Transform kills Sticky</title><style>body{margin: 0;height: 300vh;background: #1a1a2e;color: white;font-family: system-ui;}.ancestor{/* 就这一行,毁了sticky的一生 */transform:translateZ(0);/* 或者任何transform,哪怕是translateX(0) *//* 同理,filter: blur(0)也会 *//* perspective: 1000px也会 */background:rgba(255,255,255,0.1);padding: 20px;margin: 50px;min-height: 150vh;}.parent{background:rgba(255,107,107,0.3);padding: 20px;min-height: 100vh;}.sticky-element{position: sticky;top: 0;background: #ff6b6b;padding: 20px;text-align: center;font-weight: bold;z-index: 10;}.warning{background: #ffd93d;color: #333;padding: 20px;margin: 20px 0;border-radius: 8px;}/* 解决法案:去掉transform,或者用overflow替代 */.solution{transform: none;/* 去掉transform *//* 或者如果是为了开启硬件加速,用will-change代替 */will-change: transform;}</style></head><body><divclass="warning"> 注意:外层div设置了transform: translateZ(0),sticky将失效! </div><divclass="ancestor"><divclass="parent"><divclass="sticky-element"> 我设置了sticky top:0,但因为祖先有transform,我粘不住了! </div><pstyle="margin-top: 50px;">滚动试试看...</p><p>你会发现红色条块根本不会粘住,它被困在transform创建的包含块里了。</p><script>for(let i=0; i<30; i++){ document.write('<p>内容填充...</p>');}</script></div></div></body></html>

这是很多UI组件库(特别是那些用transform做动画的)埋下的暗坑。比如你用了某个轮播组件,它给容器加了transform: translate3d(...),然后你在这个容器里放个sticky导航,死活不工作,你还以为是自己的代码写错了,其实是你被组件库"坑"了。

解决办法:

  1. 要么把sticky元素挪出那个有transform的容器
  2. 要么改用will-change: transform来开启硬件加速(副作用小一些,但还是要谨慎)
  3. 如果是为了做居中,用flexbox或grid代替transform

你以为sticky只能垂直粘?横向滚动也能玩出花

大部分人用sticky都是做纵向的吸顶效果,但其实它支持四个方向:toprightbottomleft。在横向滚动(horizontal scrolling)场景下,left和rightStatement就派上用场了。

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>横向Sticky滚动</title><style>body{margin: 0;padding: 20px;font-family: system-ui;background: #0f0f23;color: white;}.horizontal-scroll-container{overflow-x: auto;white-space: nowrap;background: #1a1a2e;border-radius: 8px;padding: 20px;/* 隐藏滚动条但保留功能 */scrollbar-width: thin;scrollbar-color: #667eea #1a1a2e;}.card-row{display: inline-flex;gap: 20px;}.card{width: 300px;height: 400px;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);border-radius: 12px;padding: 20px;flex-shrink: 0;position: relative;overflow: hidden;}/* 横向粘性标签 */.sticky-label{position: sticky;left: 20px;/* 横向粘住! */background:rgba(255,255,255,0.9);color: #333;padding: 8px 16px;border-radius: 20px;display: inline-block;font-weight: bold;box-shadow: 0 2px 8px rgba(0,0,0,0.2);z-index: 10;}/* 更实用的例子:横向表格的首列固定 */.h-table-container{overflow-x: auto;margin-top: 40px;background: white;border-radius: 8px;box-shadow: 0 4px 6px rgba(0,0,0,0.1);}.h-table{border-collapse: separate;border-spacing: 0;color: #333;}.h-table th, .h-table td{padding: 16px 24px;border-bottom: 1px solid #e5e5e5;min-width: 150px;text-align: left;}/* 首列横向粘性 */.h-table th:first-child, .h-table td:first-child{position: sticky;left: 0;/* 向左粘住 */background: #f8f9fa;z-index: 20;border-right: 2px solid #dee2e6;}/* 行首的th还要顶边粘 */.h-table th:first-child{z-index: 30;/* 最高层级 */}.h-table th{background: #f8f9fa;font-weight: 600;color: #495057;position: sticky;top: 0;/* 同时支持纵向粘 */z-index: 10;}.h-table tbody tr:hover td{background: #f8f9fa;}.h-table tbody tr:hover td:first-child{background: #e9ecef;}</style></head><body><h2>横向滚动 + Sticky标签</h2><divclass="horizontal-scroll-container"><divclass="card-row"><divclass="card"><spanclass="sticky-label">热销第1名</span><h3style="margin-top: 60px;">课程标题A</h3></div><divclass="card"><spanclass="sticky-label">热销第2名</span><h3style="margin-top: 60px;">课程标题B</h3></div><divclass="card"><spanclass="sticky-label">热销第3名</span><h3style="margin-top: 60px;">课程标题C</h3></div><!-- 更多卡片 --><script>for(let i=4; i<=10; i++){ document.write(` <div> <span>热销第${i}名</span> <h3>课程标题${String.fromCharCode(64+i)}</h3> </div> `);}</script></div></div><h2style="margin-top: 40px;">双向Sticky表格(纵横都粘)</h2><divclass="h-table-container"><tableclass="h-table"><thead><tr><th>产品名称</th><th>Q1销售额</th><th>Q2销售额</th><th>Q3销售额</th><th>Q4销售额</th><th>年度总计</th><th>同比增长</th><th>市场份额</th></tr></thead><tbody><script>const products =['iPhone 15','MacBook Pro','iPad Air','Apple Watch','AirPods Pro']; products.forEach(prod=>{ document.write(` <tr> <td><strong>${prod}</strong></td> <td>¥${(Math.random()*1000).toFixed(0)}万</td> <td>¥${(Math.random()*1000).toFixed(0)}万</td> <td>¥${(Math.random()*1000).toFixed(0)}万</td> <td>¥${(Math.random()*1000).toFixed(0)}万</td> <td>¥${(Math.random()*4000).toFixed(0)}万</td> <td>+${(Math.random()*20).toFixed(1)}%</td> <td>${(Math.random()*30).toFixed(1)}%</td> </tr> `);});</script></tbody></table></div></body></html>

看到那个表格了吗?首列设置了left: 0,表头设置了top: 0,左上角的单元格同时设置了这两个属性,实现了纵横双向sticky。这种效果在以前只能用JS实现,现在纯CSS就能搞定,性能还好得多。

进阶CP:contain-intrinsic-size和overscroll-behavior

理解了sticky的基础后,咱们再说两个它的高级搭档。

首先是contain-intrinsic-size。这货和content-visibility是一对好基友。如果你有一个超长的列表或者文章,里面有一两个sticky元素,但页面加载很慢,那可能是因为浏览器在计算滚动高度时要把所有内容都渲染一遍。

用了content-visibility: auto后,视口外的元素不会渲染,但浏览器不知道这些元素多高,滚动条会跳动。这时候contain-intrinsic-size就派上用场了,它告诉浏览器:“我虽然还没渲染,但我大概这么高”,这样滚动条就是稳定的,sticky的计算也是准确的。

.long-article-section{content-visibility: auto;/* 假设这个章节大概500px高,浏览器可以先预留这个空间 */contain-intrinsic-size: 0 500px;}

再说overscroll-behavior。你有没有遇到过这种崩溃场景:你在一个弹窗里滚动,滚到顶了还继续滚,结果下面的页面跟着一起滚?这就是滚动链(scroll chaining)在作祟。给sticky容器加上overscroll-behavior: contain,就能把这个滚动"困"在当前容器里,不会影响父级页面。

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>Sticky的高级搭档</title><style>body{margin: 0;height: 200vh;background:linear-gradient(to bottom, #667eea, #764ba2);padding: 20px;font-family: system-ui;}.modal{position: fixed;top: 50%;left: 50%;transform:translate(-50%, -50%);width: 400px;height: 600px;background: white;border-radius: 12px;box-shadow: 0 20px 60px rgba(0,0,0,0.3);display: flex;flex-direction: column;}.modal-header{position: sticky;top: 0;background: #333;color: white;padding: 20px;border-radius: 12px 12px 0 0;z-index: 10;}.modal-body{flex: 1;overflow-y: auto;/* 关键属性:防止滚动传递到body */overscroll-behavior: contain;padding: 20px;}.modal-footer{position: sticky;bottom: 0;background: #f8f9fa;padding: 15px 20px;border-top: 1px solid #dee2e6;border-radius: 0 0 12px 12px;text-align: right;}.content-item{padding: 20px;margin-bottom: 10px;background: #f8f9fa;border-radius: 8px;}.btn{background: #667eea;color: white;border: none;padding: 10px 24px;border-radius: 6px;cursor: pointer;}</style></head><body><h2style="color: white;">背后的页面:试着滚动上面的弹窗到底部,看页面动不动</h2><divclass="modal"><divclass="modal-header"> 弹窗标题(Sticky Top) </div><divclass="modal-body"><p>在这个区域疯狂滚动,到底部也不会带动背后的页面</p><script>for(let i=0; i<20; i++){ document.write(`<div>列表项 ${i+1}</div>`);}</script></div><divclass="modal-footer"><buttonclass="btn">确认操作</button></div></div></body></html>

在这个弹窗例子里,头部用top: 0粘顶,底部用bottom: 0粘底,中间内容区用overscroll-behavior: contain防止滚动穿透。这三个配合起来,就是一个完美的粘性弹窗组件,体验秒杀那些用fixed模拟的半吊子实现。

最后唠一句

写到这儿,我得说句掏心窝子的话:position: sticky确实好用,但它不是万能胶。我见过有人用它做全屏滚动效果,有人用它做复杂的交错动画,还有人拿它当fixed的替代品在IE上用(在IE上它就是个static,啥也不是)。

用它做导航栏吸顶?香!用它做侧边目录跟随?香!用它做表格表头固定?真香!

但你要是想用它实现那种"卡片一张一张堆叠覆盖"的复杂效果,或者指望它在所有浏览器里表现完全一致,那可能就是给自己挖坑了。特别是涉及transform、overflow、table-cell这些特殊情况的时候,一定要多测试,Chrome里看着好好的,到Safari里可能就给你整幺蛾子。

总之,sticky是个好东西,但就像所有好东西一样,用对地方才叫神器,乱用只会让页面"粘"成一团浆糊。下次再遇到那种"我明明写了sticky为啥不生效"的情况,别急着怀疑人生,按我说的 checklist 排查一遍:top写了没?父容器overflow了没?祖先元素transform了没?容器高度够了吗?

排查完这几项,百分之九十的问题都能解决。剩下的百分之十… 那就重启试试,重启不行就填坑,填坑不行就跟产品经理说"这个需求做不了",反正锅不要甩给我这个写文章的人就行!

欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!

专栏系列(点击解锁)学习路线(点击解锁)知识定位
《微信小程序相关博客》持续更新中~结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等
《AIGC相关博客》持续更新中~AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结
《HTML网站开发相关》《前端基础入门三大核心之html相关博客》前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识
《前端基础入门三大核心之JS相关博客》前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。
通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心
《前端基础入门三大核心之CSS相关博客》介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页
《canvas绘图相关博客》Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化
《Vue实战相关博客》持续更新中~详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅
《python相关博客》持续更新中~Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具
《sql数据库相关博客》持续更新中~SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能
《算法系列相关博客》持续更新中~算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维
《IT信息技术相关博客》持续更新中~作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识
《信息化人员基础技能知识相关博客》无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方
《信息化技能面试宝典相关博客》涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面
《前端开发习惯与小技巧相关博客》持续更新中~罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等
《photoshop相关博客》持续更新中~基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结
日常开发&办公&生产【实用工具】分享相关博客》持续更新中~分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具

吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!
在这里插入图片描述

Read more

前端文件上传处理:别再让用户等待了!

前端文件上传处理:别再让用户等待了! 毒舌时刻 文件上传?听起来就像是前端工程师为了显得自己很专业而特意搞的一套复杂流程。你以为随便加个input[type=file]就能实现文件上传?别做梦了!到时候你会发现,大文件上传会导致页面崩溃,用户体验极差。 你以为FormData就能解决所有问题?别天真了!FormData在处理大文件时会导致内存溢出,而且无法显示上传进度。还有那些所谓的文件上传库,看起来高大上,用起来却各种问题。 为什么你需要这个 1. 用户体验:良好的文件上传处理可以提高用户体验,减少用户等待时间。 2. 性能优化:合理的文件上传策略可以减少服务器负担,提高上传速度。 3. 错误处理:完善的错误处理可以避免上传失败时的用户困惑。 4. 安全保障:安全的文件上传处理可以防止恶意文件上传,保障系统安全。 5. 功能丰富:支持多文件上传、拖拽上传、进度显示等功能,满足不同场景的需求。 反面教材 // 1. 简单文件上传 <input type="file&

手把手教你完成libwebkit2gtk-4.1-0安装配置(Ubuntu 22.04)

从零搞定 libwebkit2gtk-4.1-0 安装:Ubuntu 22.04 下的实战避坑指南 你有没有遇到过这样的场景?写好了一个基于 GTK 4 的本地 Web 应用,信心满满地在 Ubuntu 22.04 上运行,结果终端弹出一行红色错误: error while loading shared libraries: libwebkit2gtk-4.1.so.0: cannot open shared object file 别急——这不是你的代码出了问题,而是系统里少了关键运行时库: libwebkit2gtk-4.1-0 。 这个库是现代 Linux 桌面开发中“嵌入网页”的核心技术组件。它让你能在原生应用里无缝展示 HTML 内容,比如 Markdown

前端监控:让你的网站问题无处遁形

前端监控:让你的网站问题无处遁形 毒舌时刻 前端监控?这不是后端的事吗? "我的代码没问题,不需要监控"——结果用户反馈网站崩溃,自己却一无所知, "我有日志,还需要什么监控"——结果日志太多,根本找不到问题, "监控太复杂了,我没时间做"——结果问题频发,用户流失。 醒醒吧,前端监控是前端开发的重要组成部分,不是可有可无的! 为什么你需要这个? * 问题发现:及时发现和定位前端问题 * 性能优化:了解网站性能瓶颈 * 用户体验:了解用户真实使用情况 * 数据驱动:基于数据做出决策 反面教材 // 反面教材:没有任何监控 function App() { return ( <div> <h1>我的网站</h1&

IDA Pro+MCP+DeepSeek逆向小实战:构建AI逆向分析工作流

IDA Pro+MCP+DeepSeek逆向小实战:构建AI逆向分析工作流

一、MCP简介 ‌Model Context Protocol(MCP)是一种专为大语言模型(LLM)设计的开放协议,旨在实现LLM与外部数据源、工具的无缝集成‌。MCP通过统一的接口规范,将原本分散的API插件集成简化为“即插即用”的模式,类似于AI领域的“USB-C接口”,解决了传统API插件集成中存在的多协议适配、高开发成本等问题‌。 MCP的核心组件是: * ‌MCP Host‌(主机): 作为整个系统的起点,MCP Host是启动连接的应用程序,例如Claude Desktop、Cursor IDE等。它的主要作用是接收用户的输入(如提问、指令等),并将这些输入传递给大型语言模型(LLM)进行处理。Host在整个交互过程中扮演“桥梁”的角色,连接用户与AI模型,确保用户的需求能够被准确地传达和处理‌。 * ‌MCP Client‌(客户端):作为中间件,MCP Client负责维护与MCP Servers之间的连接。当LLM模型在处理用户请求时,如果需要访问外部资源或工具(