You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 19:48:17