滚动到页面上的特定元素

时间:2013-07-18 11:42:37

标签: javascript jquery animation scroll jquery-animate

我希望在页面的开头有4个按钮/链接,在其下面有内容。

在我输入此代码的按钮上:

<a href="#idElement1">Scroll to element 1</a>
<a href="#idElement2">Scroll to element 2</a>
<a href="#idElement3">Scroll to element 3</a>
<a href="#idElement4">Scroll to element 4</a>

在链接下会有内容:

<h2 id="idElement1">Element1</h2>
content....
<h2 id="idElement2">Element2</h2>
content....
<h2 id="idElement3">Element3</h2>
content....
<h2 id="idElement4">Element4</h2>
content....

现在正在运作,但不能让它看起来更顺畅。

我使用了这段代码,但却无法使用它。

$('html, body').animate({
    scrollTop: $("#elementID").offset().top
}, 2000);

有什么建议吗?谢谢。

编辑:和小提琴:http://jsfiddle.net/WxJLx/2/

12 个答案:

答案 0 :(得分:58)

requestAnimationFrame

超级流畅

对于平滑渲染的滚动动画,可以使用window.requestAnimationFrame() performs better with rendering而不是常规setTimeout()解决方案。

基本示例如下所示。浏览器的每个动画帧都会调用函数step,从而可以更好地管理重绘,从而提高性能。

function doScrolling(elementY, duration) { 
  var startingY = window.pageYOffset;
  var diff = elementY - startingY;
  var start;

  // Bootstrap our animation - it will get called right before next frame shall be rendered.
  window.requestAnimationFrame(function step(timestamp) {
    if (!start) start = timestamp;
    // Elapsed milliseconds since start of scrolling.
    var time = timestamp - start;
    // Get percent of completion in range [0, 1].
    var percent = Math.min(time / duration, 1);

    window.scrollTo(0, startingY + diff * percent);

    // Proceed with animation as long as we wanted it to.
    if (time < duration) {
      window.requestAnimationFrame(step);
    }
  })
}

对于元素的Y位置,在其他答案中使用函数或在我下面提到的小提琴中使用函数。

我设置了一个更复杂的功能,提供了更轻松的支持和适当滚动到最底层的元素: https://jsfiddle.net/s61x7c4e/

答案 1 :(得分:23)

刚刚在下面制作了这个仅限javascript的解决方案。

简单用法:

EPPZScrollTo.scrollVerticalToElementById('signup_form', 20);

引擎对象(您可以使用过滤器,fps值):

/**
 *
 * Created by Borbás Geri on 12/17/13
 * Copyright (c) 2013 eppz! development, LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */


var EPPZScrollTo =
{
    /**
     * Helpers.
     */
    documentVerticalScrollPosition: function()
    {
        if (self.pageYOffset) return self.pageYOffset; // Firefox, Chrome, Opera, Safari.
        if (document.documentElement && document.documentElement.scrollTop) return document.documentElement.scrollTop; // Internet Explorer 6 (standards mode).
        if (document.body.scrollTop) return document.body.scrollTop; // Internet Explorer 6, 7 and 8.
        return 0; // None of the above.
    },

    viewportHeight: function()
    { return (document.compatMode === "CSS1Compat") ? document.documentElement.clientHeight : document.body.clientHeight; },

    documentHeight: function()
    { return (document.height !== undefined) ? document.height : document.body.offsetHeight; },

    documentMaximumScrollPosition: function()
    { return this.documentHeight() - this.viewportHeight(); },

    elementVerticalClientPositionById: function(id)
    {
        var element = document.getElementById(id);
        var rectangle = element.getBoundingClientRect();
        return rectangle.top;
    },

    /**
     * Animation tick.
     */
    scrollVerticalTickToPosition: function(currentPosition, targetPosition)
    {
        var filter = 0.2;
        var fps = 60;
        var difference = parseFloat(targetPosition) - parseFloat(currentPosition);

        // Snap, then stop if arrived.
        var arrived = (Math.abs(difference) <= 0.5);
        if (arrived)
        {
            // Apply target.
            scrollTo(0.0, targetPosition);
            return;
        }

        // Filtered position.
        currentPosition = (parseFloat(currentPosition) * (1.0 - filter)) + (parseFloat(targetPosition) * filter);

        // Apply target.
        scrollTo(0.0, Math.round(currentPosition));

        // Schedule next tick.
        setTimeout("EPPZScrollTo.scrollVerticalTickToPosition("+currentPosition+", "+targetPosition+")", (1000 / fps));
    },

    /**
     * For public use.
     *
     * @param id The id of the element to scroll to.
     * @param padding Top padding to apply above element.
     */
    scrollVerticalToElementById: function(id, padding)
    {
        var element = document.getElementById(id);
        if (element == null)
        {
            console.warn('Cannot find element with id \''+id+'\'.');
            return;
        }

        var targetPosition = this.documentVerticalScrollPosition() + this.elementVerticalClientPositionById(id) - padding;
        var currentPosition = this.documentVerticalScrollPosition();

        // Clamp.
        var maximumScrollPosition = this.documentMaximumScrollPosition();
        if (targetPosition > maximumScrollPosition) targetPosition = maximumScrollPosition;

        // Start animation.
        this.scrollVerticalTickToPosition(currentPosition, targetPosition);
    }
};

答案 2 :(得分:16)

平滑滚动 - 看起来没有jQuery

基于an article on itnewb.com,我创建了一个没有外部库的 demo plunk to smoothly scroll

javascript非常简单。首先是帮助函数,以改善跨浏览器支持以确定当前位置。

function currentYPosition() {
    // Firefox, Chrome, Opera, Safari
    if (self.pageYOffset) return self.pageYOffset;
    // Internet Explorer 6 - standards mode
    if (document.documentElement && document.documentElement.scrollTop)
        return document.documentElement.scrollTop;
    // Internet Explorer 6, 7 and 8
    if (document.body.scrollTop) return document.body.scrollTop;
    return 0;
}

然后是一个确定目标元素位置的函数 - 我们要滚动到的位置。

function elmYPosition(eID) {
    var elm = document.getElementById(eID);
    var y = elm.offsetTop;
    var node = elm;
    while (node.offsetParent && node.offsetParent != document.body) {
        node = node.offsetParent;
        y += node.offsetTop;
    } return y;
}

进行滚动的核心功能

function smoothScroll(eID) {
    var startY = currentYPosition();
    var stopY = elmYPosition(eID);
    var distance = stopY > startY ? stopY - startY : startY - stopY;
    if (distance < 100) {
        scrollTo(0, stopY); return;
    }
    var speed = Math.round(distance / 100);
    if (speed >= 20) speed = 20;
    var step = Math.round(distance / 25);
    var leapY = stopY > startY ? startY + step : startY - step;
    var timer = 0;
    if (stopY > startY) {
        for ( var i=startY; i<stopY; i+=step ) {
            setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
            leapY += step; if (leapY > stopY) leapY = stopY; timer++;
        } return;
    }
    for ( var i=startY; i>stopY; i-=step ) {
        setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
        leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
    }
    return false;
}

要调用它,您只需执行以下操作即可。您可以使用id作为destination anchor的参考来创建指向其他元素的链接。

<a href="#anchor-2" 
   onclick="smoothScroll('anchor-2');">smooth scroll to the headline with id anchor-2<a/>
...
...  some content
...
<h2 id="anchor-2">Anchor 2</h2>

版权

在itnewb.com的页脚中写道如下:The techniques, effects and code demonstrated in ITNewb articles may be used for any purpose without attribution (although we recommend it)(2014-01-12)

答案 3 :(得分:4)

我已经使用了很长时间了:

function scrollToItem(item) {
    var diff=(item.offsetTop-window.scrollY)/8
    if (Math.abs(diff)>1) {
        window.scrollTo(0, (window.scrollY+diff))
        clearTimeout(window._TO)
        window._TO=setTimeout(scrollToItem, 30, item)
    } else {
        window.scrollTo(0, item.offsetTop)
    }
}

用法: scrollToItem(element)例如elementdocument.getElementById('elementid')

答案 4 :(得分:4)

@tominko答案的变化。 使用无限调用的setTimeout()时,一个更平滑的动画和已解决的问题,当某些元素无法与视口顶部对齐时。

function scrollToItem(item) {
    var diff=(item.offsetTop-window.scrollY)/20;
    if(!window._lastDiff){
        window._lastDiff = 0;
    }

    console.log('test')

    if (Math.abs(diff)>2) {
        window.scrollTo(0, (window.scrollY+diff))
        clearTimeout(window._TO)

        if(diff !== window._lastDiff){
            window._lastDiff = diff;
            window._TO=setTimeout(scrollToItem, 15, item);
        }
    } else {
        console.timeEnd('test');
        window.scrollTo(0, item.offsetTop)
    }
}

答案 5 :(得分:3)

5年前提出的问题,我当时正在与smooth scroll打交道,觉得提供一个简单的解决方案对正在寻找的人来说是值得的。所有的答案都很好,但是在这里您可以做一个简单的答案。

function smoothScroll () {
document.querySelector('.your_class or #id here').scrollIntoView({
      behavior: 'smooth'
    });
}

只需在源smoothScroll上的onClick事件上调用element函数。

注意:请检查兼容性here

答案 6 :(得分:3)

为什么不使用CSS 滚动行为属性

html {
  scroll-behavior: smooth;
}

浏览器支持也不错 https://caniuse.com/#feat=css-scroll-behavior

答案 7 :(得分:2)

你可以使用这个插件。完全符合你的要求。

http://flesler.blogspot.com/2007/10/jqueryscrollto.html

答案 8 :(得分:0)

如果需要滚动到div内的元素,那么我的解决方案基于Andrzej Sala's answer

function scroolTo(element, duration) {
    if (!duration) {
        duration = 700;
    }
    if (!element.offsetParent) {
        element.scrollTo();
    }
    var startingTop = element.offsetParent.scrollTop;
    var elementTop = element.offsetTop;
    var dist = elementTop - startingTop;
    var start;

    window.requestAnimationFrame(function step(timestamp) {
        if (!start)
            start = timestamp;
        var time = timestamp - start;
        var percent = Math.min(time / duration, 1);
        element.offsetParent.scrollTo(0, startingTop + dist * percent);

        // Proceed with animation as long as we wanted it to.
        if (time < duration) {
            window.requestAnimationFrame(step);
        }
    })
}

答案 9 :(得分:0)

您可以将for循环与window.scrollTo和setTimeout一起使用,以使用纯Javascript平滑滚动。使用我的scrollToSmoothly函数滚动到一个元素:scrollToSmoothly(elem.offsetTop)(假设elem是一个DOM元素)。

function scrollToSmoothly(pos, time){
/*Time is only applicable for scrolling upwards*/
/*Code written by hev1*/
/*pos is the y-position to scroll to (in pixels)*/
     if(isNaN(pos)){
      throw "Position must be a number";
     }
     if(pos<0){
     throw "Position can not be negative";
     }
    var currentPos = window.scrollY||window.screenTop;
    if(currentPos<pos){
    var t = 10;
       for(let i = currentPos; i <= pos; i+=10){
       t+=10;
        setTimeout(function(){
        window.scrollTo(0, i);
        }, t/2);
      }
    } else {
    time = time || 2;
       var i = currentPos;
       var x;
      x = setInterval(function(){
         window.scrollTo(0, i);
         i -= 10;
         if(i<=pos){
          clearInterval(x);
         }
     }, time);
      }
}

如果要通过元素id滚动到某个元素,可以添加此功能(以及上述功能):

function scrollSmoothlyToElementById(id){
   var elem = document.getElementById(id);
   scrollToSmoothly(elem.offsetTop);
}

演示:

<button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element<p/>
<button onClick="scrollToSmoothly(Number(0))">Scroll back to top</button>
</div>
<script>
function scrollToSmoothly(pos, time){
/*Time is only applicable for scrolling upwards*/
/*Code written by hev1*/
/*pos is the y-position to scroll to (in pixels)*/
     if(isNaN(pos)){
      throw "Position must be a number";
     }
     if(pos<0){
     throw "Position can not be negative";
     }
    var currentPos = window.scrollY||window.screenTop;
    if(currentPos<pos){
    var t = 10;
       for(let i = currentPos; i <= pos; i+=10){
       t+=10;
        setTimeout(function(){
      	window.scrollTo(0, i);
        }, t/2);
      }
    } else {
    time = time || 2;
       var i = currentPos;
       var x;
      x = setInterval(function(){
         window.scrollTo(0, i);
         i -= 10;
         if(i<=pos){
          clearInterval(x);
         }
     }, time);
      }
}
function scrollToDiv(){
  var elem = document.querySelector("div");
  scrollToSmoothly(elem.offsetTop);
}
</script>

答案 10 :(得分:0)

您还可以通过一些非常简单的方法来查看这个出色的Blog:)

https://css-tricks.com/snippets/jquery/smooth-scrolling/

像(来自博客)

// Scroll to specific values
// scrollTo is the same
window.scroll({
  top: 2500, 
  left: 0, 
  behavior: 'smooth'
});

// Scroll certain amounts from current position 
window.scrollBy({ 
  top: 100, // could be negative value
  left: 0, 
  behavior: 'smooth' 
});

// Scroll to a certain element
document.querySelector('.hello').scrollIntoView({ 
  behavior: 'smooth' 
});

,您还可以像下面那样(或通过其他方式)获得元素“ top”的位置

var e = document.getElementById(element);
var top = 0;

do {   
    top += e.offsetTop;
} while (e = e.offsetParent);

return top;

答案 11 :(得分:-2)

使用jQuery.ScrollTo

平滑滚动

要使用jQuery ScrollTo插件,您必须执行以下操作

  1. 创建href指向其他elements.id
  2. 的链接
  3. 创建要滚动到
  4. 的元素
  5. 引用jQuery和scrollTo插件
  6. 确保为每个应该进行平滑滚动的链接添加点击事件处理程序
  7. 创建链接

    <h1>Smooth Scrolling with the jQuery Plugin .scrollTo</h1>
    <div id="nav-list">
      <a href="#idElement1">Scroll to element 1</a>
      <a href="#idElement2">Scroll to element 2</a>
      <a href="#idElement3">Scroll to element 3</a>
      <a href="#idElement4">Scroll to element 4</a>
    </div>
    

    在这里创建目标元素只显示前两个,其他标题的设置方式相同。要查看另一个示例,我添加了一个返回导航a.toNav

    的链接
    <h2 id="idElement1">Element1</h2>    
    ....
    <h2 id="idElement1">Element1</h2>
    ... 
    <a class="toNav" href="#nav-list">Scroll to Nav-List</a>
    

    设置对脚本的引用。您的文件路径可能不同。

    <script src="./jquery-1.8.3.min.js"></script>
    <script src="./jquery.scrollTo-1.4.3.1-min.js"></script>
    

    全部接线

    以下代码借鉴jQuery easing plugin

    jQuery(function ($) {
        $.easing.elasout = function (x, t, b, c, d) {
            var s = 1.70158;  var p = 0; var a = c;
            if (t == 0) return b;
            if ((t /= d) == 1) return b + c;
            if (!p) p = d * .3;
            if (a < Math.abs(c)) {
                a = c;   var s = p / 4;
            } else var s = p / (2 * Math.PI) * Math.asin(c / a);
            // line breaks added to avoid scroll bar
            return a * Math.pow(2, -10 * t)  * Math.sin((t * d - s) 
                     * (2 * Math.PI) / p) + c + b;
        };            
    
        // important reset all scrollable panes to (0,0)       
        $('div.pane').scrollTo(0); 
        $.scrollTo(0);    // Reset the screen to (0,0)
        // adding a click handler for each link 
        // within the div with the id nav-list
        $('#nav-list a').click(function () {             
            $.scrollTo(this.hash, 1500, {
                easing: 'elasout'
            });
            return false;
        });   
        // adding a click handler for the link at the bottom
        $('a.toNav').click(function () { 
            var scrollTargetId = this.hash;
            $.scrollTo(scrollTargetId, 1500, {
                easing: 'elasout'
            });
            return false;
        });    
    });
    

    在plnkr.co上完全working demo

    您可以查看soucre code for the demo.

    2014年5月更新

    根据我从 another solution 发现kadaj的另一个问题。此处jQuery animate用于滚动到<div style=overflow-y: scroll>

    中的元素
     $(document).ready(function () {
        $('.navSection').on('click', function (e) {
            debugger;
            var elemId = "";    //eg: #nav2
            switch (e.target.id) {
            case "nav1":
                elemId = "#s1";
                break;
            case "nav2":
                elemId = "#s2";
                break;
            case "nav3":
                elemId = "#s3";
                break;
            case "nav4":
                elemId = "#s4";
                break;
            }
            $('.content').animate({
                scrollTop: $(elemId).parent().scrollTop() 
                        + $(elemId).offset().top 
                        - $(elemId).parent().offset().top
            }, {
                duration: 1000,
                specialEasing: { width: 'linear'
                        , height: 'easeOutBounce' },
                complete: function (e) {
                    //console.log("animation completed");
                }
            });
            e.preventDefault();
        });
      });