简体   繁体   English

如何滚动到 div 内的元素?

[英]How to scroll to an element inside a div?

I have a scrolled div and I want to have an event when I click on it, it will force this div to scroll to view an element inside.我有一个滚动的div ,当我点击它时我想有一个事件,它会强制这个div滚动以查看里面的元素。 I wrote its JavasSript like this:我这样写它的 JavasSript:

document.getElementById(chr).scrollIntoView(true);

but this scrolls all the page while scrolling the div itself.但这会在滚动div本身的同时滚动所有页面。 How to fix that?如何解决?

I want to say it like this: MyContainerDiv.getElementById(chr).scrollIntoView(true);我想这样说: MyContainerDiv.getElementById(chr).scrollIntoView(true);

You need to get the top offset of the element you'd like to scroll into view, relative to its parent (the scrolling div container):您需要获取要滚动到视图中的元素相对于其父级(滚动 div 容器)的顶部偏移量:

var myElement = document.getElementById('element_within_div');
var topPos = myElement.offsetTop;

The variable topPos is now set to the distance between the top of the scrolling div and the element you wish to have visible (in pixels).变量 topPos 现在设置为滚动 div 顶部和您希望可见的元素之间的距离(以像素为单位)。

Now we tell the div to scroll to that position using scrollTop :现在我们使用scrollTop告诉 div 滚动到那个位置:

document.getElementById('scrolling_div').scrollTop = topPos;

If you're using the prototype JS framework, you'd do the same thing like this:如果你使用原型 JS 框架,你会做同样的事情:

var posArray = $('element_within_div').positionedOffset();
$('scrolling_div').scrollTop = posArray[1];

Again, this will scroll the div so that the element you wish to see is exactly at the top (or if that's not possible, scrolled as far down as it can so it's visible).同样,这将滚动 div 以使您希望看到的元素恰好位于顶部(或者如果这不可能,则尽可能向下滚动以使其可见)。

You would have to find the position of the element in the DIV you want to scroll to, and set the scrollTop property.您必须在要滚动到的 DIV 中找到元素的位置,然后设置 scrollTop 属性。

divElem.scrollTop = 0;

Update :更新

Sample code to move up or down上移或下移的示例代码

  function move_up() {
    document.getElementById('divElem').scrollTop += 10;
  }

  function move_down() {
    document.getElementById('divElem').scrollTop -= 10;
  }

Method 1 - Smooth scrolling to an element inside an element方法 1 - 平滑滚动到元素内的元素

 var box = document.querySelector('.box'), targetElm = document.querySelector('.boxChild'); // <-- Scroll to here within ".box" document.querySelector('button').addEventListener('click', function(){ scrollToElm( box, targetElm , 600 ); }); ///////////// function scrollToElm(container, elm, duration){ var pos = getRelativePos(elm); scrollTo( container, pos.top , 2); // duration in seconds } function getRelativePos(elm){ var pPos = elm.parentNode.getBoundingClientRect(), // parent pos cPos = elm.getBoundingClientRect(), // target pos pos = {}; pos.top = cPos.top - pPos.top + elm.parentNode.scrollTop, pos.right = cPos.right - pPos.right, pos.bottom = cPos.bottom - pPos.bottom, pos.left = cPos.left - pPos.left; return pos; } function scrollTo(element, to, duration, onDone) { var start = element.scrollTop, change = to - start, startTime = performance.now(), val, now, elapsed, t; function animateScroll(){ now = performance.now(); elapsed = (now - startTime)/1000; t = (elapsed/duration); element.scrollTop = start + change * easeInOutQuad(t); if( t < 1 ) window.requestAnimationFrame(animateScroll); else onDone && onDone(); }; animateScroll(); } function easeInOutQuad(t){ return t<.5 ? 2*t*t : -1+(4-2*t)*t };
 .box{ width:80%; border:2px dashed; height:180px; overflow:auto; } .boxChild{ margin:600px 0 300px; width: 40px; height:40px; background:green; }
 <button>Scroll to element</button> <div class='box'> <div class='boxChild'></div> </div>

Method 2 - Using Element.scrollIntoView :方法 2 - 使用Element.scrollIntoView

Note that browser support isn't great for this one请注意, 浏览器支持不是很好

 var targetElm = document.querySelector('.boxChild'), // reference to scroll target button = document.querySelector('button'); // button that triggers the scroll // bind "click" event to a button button.addEventListener('click', function(){ targetElm.scrollIntoView() })
 .box { width: 80%; border: 2px dashed; height: 180px; overflow: auto; scroll-behavior: smooth; /* <-- for smooth scroll */ } .boxChild { margin: 600px 0 300px; width: 40px; height: 40px; background: green; }
 <button>Scroll to element</button> <div class='box'> <div class='boxChild'></div> </div>

Method 3 - Using CSS scroll-behavior :方法 3 - 使用 CSS 滚动行为

 .box { width: 80%; border: 2px dashed; height: 180px; overflow-y: scroll; scroll-behavior: smooth; /* <--- */ } #boxChild { margin: 600px 0 300px; width: 40px; height: 40px; background: green; }
 <a href='#boxChild'>Scroll to element</a> <div class='box'> <div id='boxChild'></div> </div>

Native JS, Cross Browser, Smooth Scroll (Update 2020)原生 JS,跨浏览器,平滑滚动(2020 更新)

Setting ScrollTop does give the desired result but the scroll is very abrupt.设置ScrollTop确实给出了预期的结果,但滚动非常突然。 Using jquery to have smooth scroll was not an option.使用jquery进行平滑滚动不是一种选择。 So here's a native way to get the job done that supports all major browsers.因此,这是一种支持所有主要浏览器的完成工作的本机方式。 Reference - caniuse参考 - caniuse

// get the "Div" inside which you wish to scroll (i.e. the container element)
const El = document.getElementById('xyz');

// Lets say you wish to scroll by 100px, 
El.scrollTo({top: 100, behavior: 'smooth'});

// If you wish to scroll until the end of the container
El.scrollTo({top: El.scrollHeight, behavior: 'smooth'});

That's it!而已!


And here's a working snippet for the doubtful -这是一个给怀疑者的工作片段 -

 document.getElementById('btn').addEventListener('click', e => { e.preventDefault(); // smooth scroll document.getElementById('container').scrollTo({top: 175, behavior: 'smooth'}); });
 /* just some styling for you to ignore */ .scrollContainer { overflow-y: auto; max-height: 100px; position: relative; border: 1px solid red; width: 120px; } body { padding: 10px; } .box { margin: 5px; background-color: yellow; height: 25px; display: flex; align-items: center; justify-content: center; } #goose { background-color: lime; }
 <!-- Dummy html to be ignored --> <div id="container" class="scrollContainer"> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div id="goose" class="box">goose</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> </div> <button id="btn">goose</button>

Update: As you can perceive in the comments, it seems that Element.scrollTo() is not supported in IE11.更新:正如您在评论中看到的那样,IE11 似乎不支持Element.scrollTo() So if you don't care about IE11 (you really shouldn't, Microsoft is retiring IE11 in June 2022), feel free to use this in all your projects.因此,如果您不关心 IE11(您真的不应该,Microsoft 将于 2022 年 6 月停用 IE11),请随时在您的所有项目中使用它。 Note that support exists for Edge!请注意,存在对 Edge 的支持! So you're not really leaving your Edge/Windows users behind ;)所以你并没有真正把你的 Edge/Windows 用户抛在后面;)

Reference 参考

To scroll an element into view of a div, only if needed, you can use this scrollIfNeeded function:要将元素滚动到 div 的视图中,仅在需要时,您可以使用此scrollIfNeeded函数:

 function scrollIfNeeded(element, container) { if (element.offsetTop < container.scrollTop) { container.scrollTop = element.offsetTop; } else { const offsetBottom = element.offsetTop + element.offsetHeight; const scrollBottom = container.scrollTop + container.offsetHeight; if (offsetBottom > scrollBottom) { container.scrollTop = offsetBottom - container.offsetHeight; } } } document.getElementById('btn').addEventListener('click', ev => { ev.preventDefault(); scrollIfNeeded(document.getElementById('goose'), document.getElementById('container')); });
 .scrollContainer { overflow-y: auto; max-height: 100px; position: relative; border: 1px solid red; width: 120px; } body { padding: 10px; } .box { margin: 5px; background-color: yellow; height: 25px; display: flex; align-items: center; justify-content: center; } #goose { background-color: lime; }
 <div id="container" class="scrollContainer"> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div id="goose" class="box">goose</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> </div> <button id="btn">scroll to goose</button>

Code should be:代码应该是:

var divElem = document.getElementById('scrolling_div');
var chElem = document.getElementById('element_within_div');
var topPos = divElem.offsetTop;
divElem.scrollTop = topPos - chElem.offsetTop;

You want to scroll the difference between child top position and div's top position.您想要滚动子顶部位置和 div 顶部位置之间的差异。

Get access to child elements using:使用以下方式访问子元素:

var divElem = document.getElementById('scrolling_div'); 
var numChildren = divElem.childNodes.length;

and so on....等等....

If you are using jQuery, you could scroll with an animation using the following:如果您使用的是 jQuery,则可以使用以下内容滚动动画:

$(MyContainerDiv).animate({scrollTop: $(MyContainerDiv).scrollTop() + ($('element_within_div').offset().top - $(MyContainerDiv).offset().top)});

The animation is optional: you could also take the scrollTop value calculated above and put it directly in the container's scrollTop property.动画是可选的:您也可以将上面计算的 scrollTop 值直接放在容器的scrollTop属性中。

We can resolve this problem without using JQuery and other libs.我们可以在不使用 JQuery 和其他库的情况下解决这个问题。

I wrote following code for this purpose:为此,我编写了以下代码:

You have similar structure ->你有类似的结构->

<div class="parent">
  <div class="child-one">

  </div>
  <div class="child-two">

  </div>
</div>

JS: JS:

scrollToElement() {
  var parentElement = document.querySelector('.parent');
  var childElement = document.querySelector('.child-two');

  parentElement.scrollTop = childElement.offsetTop - parentElement.offsetTop;
}

We can easily rewrite this method for passing parent and child as an arguments我们可以很容易地重写这个方法来传递父母和孩子作为参数

Another example of using jQuery and animate.另一个使用 jQuery 和 animate 的例子。

var container = $('#container');
var element = $('#element');

container.animate({
    scrollTop: container.scrollTop = container.scrollTop() + element.offset().top - container.offset().top
}, {
    duration: 1000,
    specialEasing: {
        width: 'linear',
        height: 'easeOutBounce'
    },
    complete: function (e) {
        console.log("animation completed");
    }
});

There are two facts :有两个事实:

1) Component scrollIntoView is not supported by safari. 1) safari 不支持组件 scrollIntoView。

2) JS framework jQuery can do the job like this: 2)JS框架jQuery可以做这样的工作:

parent = 'some parent div has css position==="fixed"' || 'html, body';

$(parent).animate({scrollTop: $(child).offset().top}, duration)

Here's a simple pure JavaScript solution that works for a target Number (value for scrollTop ), target DOM element, or some special String cases:这是一个简单的纯 JavaScript 解决方案,适用于目标 Number( scrollTop的值)、目标 DOM 元素或一些特殊的 String 情况:

/**
 * target - target to scroll to (DOM element, scrollTop Number, 'top', or 'bottom'
 * containerEl - DOM element for the container with scrollbars
 */
var scrollToTarget = function(target, containerEl) {
    // Moved up here for readability:
    var isElement = target && target.nodeType === 1,
        isNumber = Object.prototype.toString.call(target) === '[object Number]';

    if (isElement) {
        containerEl.scrollTop = target.offsetTop;
    } else if (isNumber) {
        containerEl.scrollTop = target;
    } else if (target === 'bottom') {
        containerEl.scrollTop = containerEl.scrollHeight - containerEl.offsetHeight;
    } else if (target === 'top') {
        containerEl.scrollTop = 0;
    }
};

And here are some examples of usage:以下是一些使用示例:

// Scroll to the top
var scrollableDiv = document.getElementById('scrollable_div');
scrollToTarget('top', scrollableDiv);

or或者

// Scroll to 200px from the top
var scrollableDiv = document.getElementById('scrollable_div');
scrollToTarget(200, scrollableDiv);

or或者

// Scroll to targetElement
var scrollableDiv = document.getElementById('scrollable_div');
var targetElement= document.getElementById('target_element');
scrollToTarget(targetElement, scrollableDiv);

None of other answer fixed my issue.其他答案都没有解决我的问题。

I played around with scrollIntoView arguments and managed to found a solution.我玩弄了scrollIntoView参数并设法找到了解决方案。 Setting inline to start and block to nearest prevents parent element (or entire page) to scroll:inline设置为startblocknearest可防止父元素(或整个页面)滚动:

document.getElementById(chr).scrollIntoView({
   behavior: 'smooth',
   block: 'nearest',
   inline: 'start'
});

given you have a div element you need to scroll inside, try this piece of code假设你有一个需要在里面滚动的 div 元素,试试这段代码

document.querySelector('div').scroll(x,y)

this works with me inside a div with a scroll, this should work with you in case you pointed the mouse over this element and then tried to scroll down or up.这适用于我在带有滚动的 div 中,如果您将鼠标指向此元素然后尝试向下或向上滚动,这应该适用于您。 If it manually works, it should work too如果它手动工作,它也应该工作

User Animated Scrolling用户动画滚动

Here's an example of how to programmatically scroll a <div> horizontally, without JQuery .这是一个如何在没有JQuery的情况下以编程方式水平滚动<div>的示例。 To scroll vertically, you would replace JavaScript's writes to scrollLeft with scrollTop , instead.要垂直滚动,您可以将 JavaScript 对scrollLeft的写入替换为scrollTop

JSFiddle JSFiddle

https://jsfiddle.net/fNPvf/38536/ https://jsfiddle.net/fNPvf/38536/

HTML HTML

<!-- Left Button. -->
<div style="float:left;">
    <!-- (1) Whilst it's pressed, increment the scroll. When we release, clear the timer to stop recursive scroll calls. -->
    <input type="button" value="«" style="height: 100px;" onmousedown="scroll('scroller',3, 10);" onmouseup="clearTimeout(TIMER_SCROLL);"/>
</div>
<!-- Contents to scroll. -->
<div id="scroller" style="float: left; width: 100px; height: 100px; overflow: hidden;">
    <!-- <3 -->
    <img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" alt="image large" style="height: 100px" />
</div>
<!-- Right Button. -->
<div style="float:left;">
    <!-- As (1). (Use a negative value of 'd' to decrease the scroll.) -->
    <input type="button" value="»" style="height: 100px;" onmousedown="scroll('scroller',-3, 10);" onmouseup="clearTimeout(TIMER_SCROLL);"/>
</div>

JavaScript JavaScript

// Declare the Shared Timer.
var TIMER_SCROLL;
/** 
Scroll function. 
@param id  Unique id of element to scroll.
@param d   Amount of pixels to scroll per sleep.
@param del Size of the sleep (ms).*/
function scroll(id, d, del){
    // Scroll the element.
    document.getElementById(id).scrollLeft += d;
    // Perform a delay before recursing this function again.
    TIMER_SCROLL = setTimeout("scroll('"+id+"',"+d+", "+del+");", del);
 }

Credit to Dux . 归功于 Dux


Auto Animated Scrolling自动动画滚动

In addition, here are functions for scrolling a <div> fully to the left and right.此外,这里还有将<div>完全左右滚动的函数。 The only thing we change here is we make a check to see if the full extension of the scroll has been utilised before making a recursive call to scroll again.我们在这里唯一改变的是,在再次递归调用滚动之前,我们检查滚动的完整扩展是否已被利用。

JSFiddle JSFiddle

https://jsfiddle.net/0nLc2fhh/1/ https://jsfiddle.net/0nLc2fhh/1/

HTML HTML

<!-- Left Button. -->
<div style="float:left;">
    <!-- (1) Whilst it's pressed, increment the scroll. When we release, clear the timer to stop recursive scroll calls. -->
    <input type="button" value="«" style="height: 100px;" onclick="scrollFullyLeft('scroller',3, 10);"/>
</div>
<!-- Contents to scroll. -->
<div id="scroller" style="float: left; width: 100px; height: 100px; overflow: hidden;">
  <!-- <3 -->
  <img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" alt="image large" style="height: 100px" />
</div>
<!-- Right Button. -->
<div style="float:left;">
    <!-- As (1). (Use a negative value of 'd' to decrease the scroll.) -->
    <input type="button" value="»" style="height: 100px;" onclick="scrollFullyRight('scroller',3, 10);"/>
</div>

JavaScript JavaScript

// Declare the Shared Timer.
var TIMER_SCROLL;
/** 
Scroll fully left function; completely scrolls  a <div> to the left, as far as it will go.
@param id  Unique id of element to scroll.
@param d   Amount of pixels to scroll per sleep.
@param del Size of the sleep (ms).*/
function scrollFullyLeft(id, d, del){
    // Fetch the element.
    var el = document.getElementById(id);
    // Scroll the element.
    el.scrollLeft += d;
    // Have we not finished scrolling yet?
    if(el.scrollLeft < (el.scrollWidth - el.clientWidth)) {
        TIMER_SCROLL = setTimeout("scrollFullyLeft('"+id+"',"+d+", "+del+");", del);
    }
}

/** 
Scroll fully right function; completely scrolls  a <div> to the right, as far as it will go.
@param id  Unique id of element to scroll.
@param d   Amount of pixels to scroll per sleep.
@param del Size of the sleep (ms).*/
function scrollFullyRight(id, d, del){
    // Fetch the element.
    var el = document.getElementById(id);
    // Scroll the element.
    el.scrollLeft -= d;
    // Have we not finished scrolling yet?
    if(el.scrollLeft > 0) {
        TIMER_SCROLL = setTimeout("scrollFullyRight('"+id+"',"+d+", "+del+");", del);
    }
}

This is what has finally served me这就是最终为我服务的东西

/** Set parent scroll to show element
 * @param element {object} The HTML object to show
 * @param parent {object} The HTML object where the element is shown  */
var scrollToView = function(element, parent) {
    //Algorithm: Accumulate the height of the previous elements and add half the height of the parent
    var offsetAccumulator = 0;
    parent = $(parent);
    parent.children().each(function() {
        if(this == element) {
            return false; //brake each loop
        }
        offsetAccumulator += $(this).innerHeight();
    });
    parent.scrollTop(offsetAccumulator - parent.innerHeight()/2);
}

I needed to scroll a dynamically loading element on a page so my solution was a little more involved.我需要在页面上滚动动态加载元素,因此我的解决方案涉及更多。

This will work on static elements that are not lazy loading data and data being dynamically loaded.这将适用于不是延迟加载数据和动态加载数据的静态元素。

const smoothScrollElement = async (selector: string, scrollBy = 12, prevCurrPos = 0) => {
    const wait = (timeout: number) => new Promise(resolve => setTimeout(resolve, timeout));
    const el = document.querySelector(selector) as HTMLElement;
    let positionToScrollTo = el.scrollHeight;
    let currentPosition = Math.floor(el.scrollTop) || 0;
    let pageYOffset = (el.clientHeight + currentPosition);
    if (positionToScrollTo == pageYOffset) {
        await wait(1000);
    }
    if ((prevCurrPos > 0 && currentPosition <= prevCurrPos) !== true) {
        setTimeout(async () => {
            el.scrollBy(0, scrollBy);
            await smoothScrollElement(selector, scrollBy, currentPosition);
        }, scrollBy);
    }
};

浏览器会自动滚动到获得焦点的元素,因此您还可以将需要滚动到的元素包装到<a>...</a>中,然后在需要滚动时设置焦点在那a

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM