【问题标题】:Smooth scroll anchor links WITHOUT jQuery没有 jQuery 的平滑滚动锚链接
【发布时间】:2013-07-17 23:18:15
【问题描述】:

是否可以使用平滑滚动来锚定链接但没有 jQuery?我正在创建一个新网站,我不想使用jQuery

【问题讨论】:

    标签: javascript hyperlink anchor


    【解决方案1】:

    扩展这个答案:https://stackoverflow.com/a/8918062/3851798

    定义你的scrollTo函数后,你可以在函数中传入你想要scrollTo的元素。

    function scrollTo(element, to, duration) {
        if (duration <= 0) return;
        var difference = to - element.scrollTop;
        var perTick = difference / duration * 10;
    
        setTimeout(function() {
            element.scrollTop = element.scrollTop + perTick;
            if (element.scrollTop === to) return;
            scrollTo(element, to, duration - 10);
        }, 10);
    }
    

    如果你有一个 id="footer" 的 div

    <div id="footer" class="categories">…</div>
    

    在您运行滚动的脚本中,您可以运行它,

    elmnt = document.getElementById("footer");
    scrollTo(document.body, elmnt.offsetTop, 600);
    

    你有它。没有 jQuery 的平滑滚动。您实际上可以在浏览器的控制台上使用该代码并根据自己的喜好对其进行微调。

    【讨论】:

    • 我必须将 document.documentElement 传递给函数而不是 document.body 才能使其工作
    • document.body 用于边缘,document.documentElement 用于 chrome。
    【解决方案2】:

    使用此处的函数:JavaScript animation 并对其进行修改以修改属性(不仅仅是样式的属性),您可以尝试以下操作:

    演示: http://jsfiddle.net/7TAa2/1/

    只是说...

    function animate(elem, style, unit, from, to, time, prop) {
      if (!elem) {
        return;
      }
      var start = new Date().getTime(),
        timer = setInterval(function() {
          var step = Math.min(1, (new Date().getTime() - start) / time);
          if (prop) {
            elem[style] = (from + step * (to - from)) + unit;
          } else {
            elem.style[style] = (from + step * (to - from)) + unit;
          }
          if (step === 1) {
            clearInterval(timer);
          }
        }, 25);
      if (prop) {
        elem[style] = from + unit;
      } else {
        elem.style[style] = from + unit;
      }
    }
    
    window.onload = function() {
      var target = document.getElementById("div5");
      animate(document.scrollingElement || document.documentElement, "scrollTop", "", 0, target.offsetTop, 2000, true);
    };
    div {
      height: 50px;
    }
    <div id="div1">asdf1</div>
    <div id="div2">asdf2</div>
    <div id="div3">asdf3</div>
    <div id="div4">asdf4</div>
    <div id="div5">asdf5</div>
    <div id="div6">asdf6</div>
    <div id="div7">asdf7</div>
    <div id="div8">asdf8</div>
    <div id="div9">asdf9</div>
    <div id="div10">asdf10</div>
    <div id="div10">asdf11</div>
    <div id="div10">asdf12</div>
    <div id="div10">asdf13</div>
    <div id="div10">asdf14</div>
    <div id="div10">asdf15</div>
    <div id="div10">asdf16</div>
    <div id="div10">asdf17</div>
    <div id="div10">asdf18</div>
    <div id="div10">asdf19</div>
    <div id="div10">asdf20</div>

    【讨论】:

    • 致那些尝试过的人:这是很棒的脚本,但不要指望任何缓和或漂亮的补间。这是一种原始的 steppy 动画。
    • @mtness 它在 Firefox 中运行良好(代码,可能不是演示)。您需要更新您正在制作动画的元素,因为 Firefox 显然不喜欢更改 document.bodyscrollTop(通过快速调试和谷歌搜索来解决这个问题)。试试这个:jsfiddle.net/zpu16nen(我会更新这篇文章)
    【解决方案3】:

    实际上,还有更轻量级和简单的方法可以做到这一点: https://codepen.io/ugg0t/pen/mqBBBY

    function scrollTo(element) {
      window.scroll({
        behavior: 'smooth',
        left: 0,
        top: element.offsetTop
      });
    }
    
    document.getElementById("button").addEventListener('click', () => {
      scrollTo(document.getElementById("8"));
    });
    div {
      width: 100%;
      height: 200px;
      background-color: black;
    }
    
    div:nth-child(odd) {
      background-color: white;
    }
    
    button {
      position: absolute;
      left: 10px;
      top: 10px;
    }
    <div id="1"></div>
    <div id="2"></div>
    <div id="3"></div>
    <div id="4"></div>
    <div id="5"></div>
    <div id="6"></div>
    <div id="7"></div>
    <div id="8"></div>
    <div id="9"></div>
    <div id="10"></div>
    <button id="button">Button</button>

    【讨论】:

    • 唯一的问题是你应该使用element.offsetTop而不是element.getBoundingClientRect().top + window.scrollY
    • @zdolny 现在可以在原生 Edge 上运行,但是对于 IE,你需要一个 polyfill - github.com/iamdustan/smoothscroll
    • @ekfuhrmann I don't care about IE 但它在 Safari 中有效吗?还是我也不应该关心 Safari?
    • 这和简单的添加scroll-behavior: smooth一样,在目前大部分浏览器中都很好用,但是Safari没有采用。
    【解决方案4】:

    使用这个:

    let element = document.getElementById("box");
    
    element.scrollIntoView();
    element.scrollIntoView(false);
    element.scrollIntoView({block: "end"});
    element.scrollIntoView({behavior: "instant", block: "end", inline: "nearest"});
    

    演示https://jsfiddle.net/anderpo/x8ucc5ak/1/

    【讨论】:

    • 尝试添加一些解释
    • 非常有趣的解决方案(+1),但目前在 IE 中不被支持!
    • scroll-behavior: smooth 大致相同,Safari(或 IE,但更重要的是现代 Safari)不支持。
    【解决方案5】:

    带有:target 选择器的CSS3 过渡可以在没有任何JS hack 的情况下提供很好的结果。我只是在考虑是否要实现这一点,但如果没有 Jquery,它确实会有点混乱。详情请见this question

    【讨论】:

    • 为什么不包括最重要的部分? html { scroll-behavior: smooth; } - 如此简单,如果您可以避开 IE 支持,那么您可以享受这种享受
    • IE 支持是一回事,但它在现代 Safari 中也不起作用。
    【解决方案6】:

    使用 requestAnimationFrame 的 Vanilla js 变体,带有缓动和支持的所有浏览器:

    const requestAnimationFrame = window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame;
    
    function scrollTo(to) {
        const start = window.scrollY || window.pageYOffset
        const time = Date.now()
        const duration = Math.abs(start - to) / 3;
    
        (function step() {
            var dx = Math.min(1, (Date.now() - time) / duration)
            var pos = start + (to - start) * easeOutQuart(dx)
    
            window.scrollTo(0, pos)
    
            if (dx < 1) {
                requestAnimationFrame(step)
            }
        })()
    }
    

    支持任何easing

    【讨论】:

    • 非常好的答案.. 在 safari、chrome 上测试。
    【解决方案7】:

    在这里试试这个代码:

    window.scrollTo({
            top: 0,
            left: 0,
            behavior: 'smooth'
        });
    

    【讨论】:

      【解决方案8】:

      使用 polyfill 实现平滑滚动行为...

      例子:

      document.querySelectorAll('a[href^="#"]').addEventListener("click", function(event) {
        event.preventDefault();
        document.querySelector(this.getAttribute("href")).scrollIntoView({ behavior: "smooth" });
      });
      

      存储库:https://github.com/iamdustan/smoothscroll

      【讨论】:

      • querySelectorAll 不能监听点击事件,它返回一个数组。
      【解决方案9】:

      这是一个很老的问题,但我认为现在 CSS 支持平滑滚动很重要,因此不需要任何脚本:

      html {
        scroll-behavior: smooth;
      }
      

      截至 2019 年,此属性仍然不支持 Safari 或 IE/Edge,因此要获得完整的跨浏览器支持,您仍然必须使用脚本。

      【讨论】:

        【解决方案10】:

        目前我最喜欢的滚动到库是Zenscroll,因为wide range of features 和小尺寸(目前只有3.17kb)。

        将来使用原生的scrollIntoView 功能可能更有意义,但由于缺乏 IE 支持,现在大多数生产站点都必须对它进行 polyfill,我建议在所有情况下都使用 Zenscroll .

        【讨论】:

        • 阿姆!这是目前唯一按我预期工作的库。谢谢!
        • @zach-sauchier 你现在还推荐使用这个库还是转向原生解决方案?
        • 仍然没有原生解决方案。现在有很多替代品,比如 GSAP 的 ScrollToPlugin
        【解决方案11】:

        是@Ian的升级版

        // Animated scroll with pure JS
        // duration constant in ms
        const animationDuration = 600;
        // scrollable layout
        const layout = document.querySelector('main');
        const fps = 12;  // in ms per scroll step, less value - smoother animation
        function scrollAnimate(elem, style, unit, from, to, time, prop) {
            if (!elem) {
                return;
            }
            var start = new Date().getTime(),
                timer = setInterval(function () {
                    var step = Math.min(1, (new Date().getTime() - start) / time);
                    var value =  (from + step * (to - from)) + unit;
                    if (prop) {
                        elem[style] = value;
                    } else {
                        elem.style[style] = value;
                    }
                    if (step === 1) {
                        clearInterval(timer);
                    }
                }, fps);
            if (prop) {
                elem[style] = from + unit;
            } else {
                elem.style[style] = from + unit;
            }
        }
        
        function scrollTo(hash) {
            const target = document.getElementById(hash);
            const from = window.location.hash.substring(1) || 'start';
            const offsetFrom = document.getElementById(from).offsetTop;
            const offsetTo = target.offsetTop;
            scrollAnimate(layout,
                "scrollTop", "", offsetFrom, offsetTo, animationDuration, true);
            setTimeout(function () {
              window.location.hash = hash;
            }, animationDuration+25)
        };
        
        // add scroll when click on menu items 
        var menu_items = document.querySelectorAll('a.mdl-navigation__link');
        menu_items.forEach(function (elem) {
            elem.addEventListener("click",
                function (e) {
                    e.preventDefault();
                    scrollTo(elem.getAttribute('href').substring(1));
                });
        });
        
        // scroll when open link with anchor 
        window.onload = function () {
            if (window.location.hash) {
                var target = document.getElementById(window.location.hash.substring(1));
                scrollAnimate(layout, "scrollTop", "", 0, target.offsetTop, animationDuration, true);
            }
        }
        

        【讨论】:

          【解决方案12】:

          对于 2019 年的任何人, 首先,添加一个事件监听器

            document.getElementById('id').addEventListener('click', () => scrollTo())
          

          然后你以元素为目标并顺利进入它

          function scrollTo() {
              let target = document.getElementById('target');
              target.scrollIntoView({
                  behavior: "smooth", 
                  block: "end", 
                  inline: "nearest"
              })
          }
          

          【讨论】:

          • 注意:这个例子的支持非常有限。如果不使用诸如 Babel caniuse.com/#feat=arrow-functions 之类的转译器,箭头函数将无法在任何版本的 IE、Opera Mini、Blackberry Browser、IE Mobile 和 QQ 浏览器中使用,并且“平滑”行为也非常重要截至 2019 年缺乏支持caniuse.com/#search=scrollIntoView
          【解决方案13】:

          基于MDN docs 的滚动选项,我们可以使用以下代码:

          element.scrollTo({
            top: 100,
            left: 100,
            behavior: 'smooth'
          });
          

          其实behavior键可以接受smoothauto变量。第一个用于平滑运动,第二个用于单跳。 ‍‍

          【讨论】:

            【解决方案14】:

            这是一个简单的纯 JavaScript 解决方案。它利用 CSS 属性 scroll-behavior: smooth

            function scroll_to(id) {       
                document.documentElement.style.scrollBehavior = 'smooth'
                element = document.createElement('a');
                element.setAttribute('href', id)
                element.click();
            }
            

            用法

            假设我们有 10 个 div:

            <div id='df7ds89' class='my_div'>ONE</div>
            <div id='sdofo8f' class='my_div'>TWO</div>
            <div id='34kj434' class='my_div'>THREE</div>
            <div id='gbgfh98' class='my_div'>FOUR</div>
            <div id='df89sdd' class='my_div'>FIVE</div>
            <div id='34l3j3r' class='my_div'>SIX</div>
            <div id='56j5453' class='my_div'>SEVEN</div>
            <div id='75j6h4r' class='my_div'>EIGHT</div>
            <div id='657kh54' class='my_div'>NINE</div>
            <div id='43kjhjh' class='my_div'>TEN</div>
            

            我们可以滚动到选择的 ID

            scroll_to('#657kh54')
            

            您只需在您的点击事件中调用此函数(例如,点击按钮然后滚动到 div #9)。

            结果

            当然,它在现实生活中看起来要流畅得多。

            FIDDLE

            很遗憾,从 2019

            起,IE 和 Safari 不支持 scrollBehavior = 'smooth'

            MDN Web Docs

            【讨论】:

              【解决方案15】:

              更全面的平滑滚动方法列表见我的回答here


              要在准确的时间内滚动到某个位置,可以使用window.requestAnimationFrame,每次计算适当的当前位置。在不支持requestAnimationFrame 时,可以使用setTimeout 达到类似的效果。

              /*
                 @param pos: the y-position to scroll to (in pixels)
                 @param time: the exact amount of time the scrolling will take (in milliseconds)
              */
              function scrollToSmoothly(pos, time) {
                  var currentPos = window.pageYOffset;
                  var start = null;
                  if(time == null) time = 500;
                  pos = +pos, time = +time;
                  window.requestAnimationFrame(function step(currentTime) {
                      start = !start ? currentTime : start;
                      var progress = currentTime - start;
                      if (currentPos < pos) {
                          window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
                      } else {
                          window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
                      }
                      if (progress < time) {
                          window.requestAnimationFrame(step);
                      } else {
                          window.scrollTo(0, pos);
                      }
                  });
              }
              

              演示:

              function scrollToSmoothly(pos, time) {
                  var currentPos = window.pageYOffset;
                  var start = null;
                  if(time == null) time = 500;
                  pos = +pos, time = +time;
                  window.requestAnimationFrame(function step(currentTime) {
                      start = !start ? currentTime : start;
                      var progress = currentTime - start;
                      if (currentPos < pos) {
                          window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
                      } else {
                          window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
                      }
                      if (progress < time) {
                          window.requestAnimationFrame(step);
                      } else {
                          window.scrollTo(0, pos);
                      }
                  });
              }
              
              document.getElementById("toElement").addEventListener('click', function(e) {
                var elem = document.querySelector("div");
                scrollToSmoothly(elem.offsetTop);
              });
              document.getElementById("toTop").addEventListener('click', function(e){
                scrollToSmoothly(0, 700);
              });
              <button id="toElement">Scroll To Element</button>
              <div style="margin: 1000px 0px; text-align: center;">Div element
                <button id="toTop">Scroll back to top</button>
              </div>

              对于更复杂的情况,可以使用SmoothScroll.js library,它可以处理垂直和水平平滑滚动、在其他容器元素内滚动、不同的缓动行为、从当前位置相对滚动等等。

              document.getElementById("toElement").addEventListener('click', function(e) {
                smoothScroll({toElement: document.querySelector('div'), duration: 500});
              });
              document.getElementById("toTop").addEventListener('click', function(e){
                smoothScroll({yPos: 0, duration: 700});
              });
              <script src="https://cdn.jsdelivr.net/gh/LieutenantPeacock/SmoothScroll@1.2.0/src/smoothscroll.min.js" integrity="sha384-UdJHYJK9eDBy7vML0TvJGlCpvrJhCuOPGTc7tHbA+jHEgCgjWpPbmMvmd/2bzdXU" crossorigin="anonymous"></script>
              <button id="toElement">Scroll To Element</button>
              <div style="margin: 1000px 0px; text-align: center;">Div element
                <button id="toTop">Scroll back to top</button>
              </div>

              或者,您可以将选项对象传递给 window.scroll 滚动到特定的 x 和 y 位置,window.scrollBy 从当前位置滚动一定量:

              // 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' 
              });
              

              演示:

              <button onClick="scrollToDiv()">Scroll To Element</button>
              <div style="margin: 500px 0px;">Div</div>
              <script>
              function scrollToDiv(){
              var elem = document.querySelector("div");
              window.scroll({
                    top: elem.offsetTop, 
                    left: 0, 
                    behavior: 'smooth' 
              });
              }
              </script>

              现代浏览器支持scroll-behavior CSS property,可用于平滑滚动文档(无需JavaScript)。锚标签可以通过给锚标签一个href# 加上要滚动到的元素的id 来使用)。您还可以为div 等特定容器设置scroll-behavior 属性,以使其内容平滑滚动。

              html, body{
                scroll-behavior: smooth;
              }
              <a href="#elem">Scroll To Element</a>
              <div id="elem" style="margin: 500px 0px;">Div</div>

              【讨论】:

                【解决方案16】:

                没有 jQuery

                const links = document.querySelectorAll('header nav ul a')
                
                for (const link of links) {
                  link.onclick = function clickHandler(e) {
                    e.preventDefault()
                    const href = this.getAttribute('href')
                    document.querySelector(href).scrollIntoView({ behavior: 'smooth' })
                  }
                }
                  body {
                    background-color: black;
                    height:7000px
                  }
                
                  header {
                    margin-top: 1.3rem;
                    margin-bottom: 25rem;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                  }
                
                  nav ul {
                    display: flex;
                  }
                
                  nav ul li {
                    all: unset;
                    margin: 2rem;
                    cursor: pointer;
                  }
                
                  nav ul li a {
                    all: unset;
                    font: bold 1.8rem robto;
                    color: white;
                    letter-spacing: 1px;
                    cursor: pointer;
                    padding-top: 3rem;
                    padding-bottom: 2rem;
                  }
                
                  #team,
                  #contact,
                  #about {
                    background-color: #e2df0d;
                    width: 100%;
                    height: 35rem;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    color: black;
                    font: bold 4rem roboto;
                    letter-spacing: 6.2px;
                    margin-top: 70rem;
                
                  }
                <header>
                  <!-- NavBar -->
                  <nav>
                    <ul>
                      <li><a href="#team">Team</a></li>
                      <li><a href="#contact">Contact</a></li>
                      <li><a href="#about">About</a></li>
                    </ul>
                  </nav>
                </header>
                
                <!-- ----------- Team ----------------------- -->
                <div id="team">
                  <h2>Team</h2>
                </div>
                
                <!-- ----------- Contact ----------------------- -->
                <div id="contact">
                  <h2>Contact</h2>
                </div>
                
                <!-- ----------- About ----------------------- -->
                <div id="about">
                  <h2>About</h2>
                </div>

                或仅使用 CSS,但尚不支持所有浏览器

                html {scroll-behavior: smooth}

                【讨论】:

                  【解决方案17】:

                  如果您想将所有深层链接# 设置为平滑滚动,您可以这样做:

                  const allLinks = document.querySelectorAll('a[href^="#"]')
                  allLinks.forEach(link => {
                  
                    const 
                      targetSelector = link.getAttribute('href'),
                      target = document.querySelector(targetSelector)
                  
                    if (target) {
                      link.addEventListener('click', function(e) {
                  
                      e.preventDefault()
                  
                      const top = target.offsetTop // consider decreasing your main nav's height from this number
                  
                      window.scroll({
                        behavior: 'smooth',
                        left: 0,
                        top: top
                      });
                  
                    })
                  }
                  })
                  

                  还要考虑主导航高度的示例代码(此代码位于声明 top const 的位置):

                  const 
                    mainHeader = document.querySelector('header#masthead'), //change to your correct main nav selector
                    mainHeaderHeight = mainHeader.offsetHeight,
                    // now calculate top like this:
                    top = target.offsetTop - mainHeaderHeight 
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-12-25
                    相关资源
                    最近更新 更多