X轴滚动溢出元素至可视区:jQuery转原生JS实现方案
问题解决:原生JS实现溢出按钮的滚动定位
我有一个展示游戏信息的栏位,设置了最大宽度限制,里面的按钮出现了溢出。需要实现:点击按钮时,先把最后一个“Bring Me Into View”按钮沿X轴滚动到可视区域,之后再让“Overview”按钮向左滚动到溢出区域。之前用jQuery写的滚动逻辑有问题,现在要换成原生JavaScript实现。
解决方案
1. 原生JS替代jQuery平滑滚动
用requestAnimationFrame配合缓动函数实现平滑滚动,模拟jQuery的swing效果,避免生硬的滚动过渡。
2. 精准定位目标元素
通过getBoundingClientRect()获取元素和容器的位置信息,计算精确的滚动距离,确保目标元素刚好进入可视区。
3. 实现滚动序列
用Promise封装滚动函数,让两个滚动动作按顺序执行:先滚动到最后一个按钮,完成后再反向滚动让Overview溢出。
完整代码实现
HTML(移除jQuery依赖)
<div class="jpr-top-bar"> <div class=""> <div class="j-search-suggestions j-no-touch j-screen-wide j-wrap-wide"> <section class="j-search-sug j-has-image j-has-subheading"> <div class="j-search-sug-inner"> <header class="j-search-sug-header"> <div class="j-search-sug-thumb"><img alt="" src=""></div> <div class="j-search-sug-headings"><a class="j-search-sug-title-link" href=""> <h2 class="j-search-sug-heading">Minecraft</h2> <h3 class="j-search-sug-subheading">Video Game</h3> </a></div> </header> <div class="j-search-sug-items"> <div class="j-search-sug-items-view j-overflow-gradient-parent"> <div class="j-search-sug-scroller"> <ul> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Overview</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Gameplay</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Buy</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Reviews</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Trailers</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Guides</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Tips & strategies</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Cheats</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Sequels</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Mojang</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Overflow</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Overflow</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Overflow</span></a></li> <li class="j-search-sug-item"><a href="" class="j-search-sug-link"><span>Bring Me Into View</span></a></li> </ul> </div> </div> </div> </div> </section> </div> </div> </div> <button onclick="scrollOverflowingElementsIntoView()"> Test </button>
CSS(保留原有样式)
.jpr-top-bar { width: 1000px; } .j-search-suggestions { background: none; } .j-search-suggestions, .j-search-suggestions *, .j-search-suggestions ::before, .j-search-suggestions ::after { box-sizing: border-box; } .j-search-suggestions { color: var(--j-foreground); background: var(--j-background); border-bottom: 1px solid var(--j-border); padding: 0 0 15px; margin: 0 0 15px; } .j-search-sug, .j-search-suggestions { position: relative; max-width: 100%; text-align: left; word-break: normal; } .j-search-sug { padding: 0; margin: 0 auto; } .j-search-suggestions, .j-search-suggestions *, .j-search-suggestions ::before, .j-search-suggestions ::after { box-sizing: border-box; } .j-search-sug, .j-search-suggestions { position: relative; max-width: 100%; text-align: left; word-break: normal; } .j-wrap-wide .j-search-sug-inner { position: relative; display: flex; align-items: center; justify-content: flex-start; width: 100%; margin: 0 auto; } .j-search-suggestions, .j-search-suggestions *, .j-search-suggestions ::before, .j-search-suggestions ::after { box-sizing: border-box; } .j-wrap-wide .j-search-sug-thumb { min-width: 92px; min-height: 92px; } .j-wrap-wide .j-search-sug-thumb { min-width: 46px; } .j-search-sug-thumb { max-width: 32%; } .j-search-suggestions, .j-search-suggestions *, .j-search-suggestions ::before, .j-search-suggestions ::after { box-sizing: border-box; } .j-wrap-wide .j-search-sug-thumb img { min-width: 92px; min-height: 92px; } .j-wrap-wide .j-search-sug-thumb img { width: 46px; height: 46px; border-radius: 6px; } .j-search-sug-thumb img { display: block; max-width: 100%; width: auto; height: auto; -o-object-fit: cover; object-fit: cover; margin: 0; } .j-search-suggestions, .j-search-suggestions *, .j-search-suggestions ::before, .j-search-suggestions ::after { box-sizing: border-box; } .j-wrap-wide .j-search-sug-headings { padding: 0 0 0 11.09px; max-width: 239px; } .j-search-sug-headings { padding: 0 0 0 15px; } .j-search-suggestions, .j-search-suggestions *, .j-search-suggestions ::before, .j-search-suggestions ::after { box-sizing: border-box; } a.j-search-sug-title-link { text-decoration: none; pointer-events: none; cursor: default; text-decoration: none; } a.j-search-sug-title-link { text-decoration: none; } .j-search-suggestions, .j-search-suggestions *, .j-search-suggestions ::before, .j-search-suggestions ::after { box-sizing: border-box; } .j-wrap-wide .j-search-sug-heading { font-size: 29.034px; line-height: 1.0; } .j-search-sug-heading { color: var(--j-foregroundEmphasis); } .j-search-sug-heading, .j-search-sug-subheading { padding: 0; margin: 0; font-weight: normal; line-height: normal; } .j-search-sug-items { position: relative; max-width: 100%; min-width: 0; width: auto; } .j-wrap-wide .j-search-sug-items-view { height: 36px; } .j-search-sug-items-view { position: relative; height: 38px; overflow: hidden; width: 100%; padding: 0; margin: 0; } .j-search-sug-scroller { position: relative; height: calc(38px + 130px); padding: 0; margin: 0; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; max-width: none; width: auto; } .j-search-sug-items ul { list-style: none; display: inline-flex; align-items: stretch; justify-content: flex-start; padding: 0; margin: 0; max-width: none; width: auto; height: 100%; } .j-search-suggestions, .j-search-suggestions *, .j-search-suggestions ::before, .j-search-suggestions ::after { box-sizing: border-box; } .j-search-sug-items li { display: inline-block; margin: 0; padding: 0 6px 0 0; line-height: 1; } .j-wrap-wide .j-search-sug-items a.j-search-sug-link { height: 36px; font-size: 14px; padding: 0 15px; }
原生JavaScript代码
// 缓动函数:模拟jQuery的swing效果 function swingEase(t, b, c, d) { return c * (0.5 - Math.cos(t / d * Math.PI) / 2) + b; } // 平滑滚动函数,返回Promise控制执行顺序 function smoothScroll(element, targetScrollLeft, duration = 500) { return new Promise((resolve) => { const startScrollLeft = element.scrollLeft; const distance = targetScrollLeft - startScrollLeft; let startTime = null; function animation(currentTime) { if (!startTime) startTime = currentTime; const timeElapsed = currentTime - startTime; const progress = Math.min(timeElapsed / duration, 1); const easedProgress = swingEase(progress, 0, 1, 1); element.scrollLeft = startScrollLeft + distance * easedProgress; if (timeElapsed < duration) { requestAnimationFrame(animation); } else { resolve(); } } requestAnimationFrame(animation); }); } async function scrollOverflowingElementsIntoView() { console.log("scrolling"); const scroller = document.querySelector('.j-search-sug-scroller'); const targetBtn = document.querySelector('.j-search-sug-item:last-child .j-search-sug-link'); const overviewBtn = document.querySelector('.j-search-sug-item:first-child .j-search-sug-link'); // 第一步:滚动到最后一个按钮,使其完全进入可视区 const targetRect = targetBtn.getBoundingClientRect(); const scrollerRect = scroller.getBoundingClientRect(); const targetScrollLeft = scroller.scrollLeft + (targetRect.right - scrollerRect.right); await smoothScroll(scroller, targetScrollLeft); // 第二步:反向滚动,让Overview按钮溢出到左侧 const overviewRect = overviewBtn.getBoundingClientRect(); const overviewScrollLeft = scroller.scrollLeft - (overviewRect.right - scrollerRect.left); await smoothScroll(scroller, overviewScrollLeft); }
内容的提问来源于stack exchange,提问作者Jimmison Johnson
相关产品推荐
相关产品推荐

