使用纯JS滚动到使用div(不是窗口)的位置

问题描述:

PURE JS只能请 - 没有JQUERY

PURE JS ONLY PLEASE - NO JQUERY

我有一个带溢出滚动的div,窗口(html / body)永远不会溢出。

I have a div with overflow scroll, the window (html/body) never overflows itself.

我有一个锚链接列表,想要点击它们时滚动到一个位置。

I have a list of anchor links and want to scroll to a position when they're clicked.

基本上只是寻找锚点滚动来自div,而不是窗口。

Basically just looking for anchor scrolling from within a div, not window.

window.scrollTo等不起作用,因为窗口永远不会溢出。

window.scrollTo etc. don't work as the window never actually overflows.

简单测试用例 http://codepen.io/mildrenben/pen/RPyzqm

JADE

nav
  a(data-goto="#1") 1
  a(data-goto="#2") 2
  a(data-goto="#3") 3
  a(data-goto="#4") 4
  a(data-goto="#5") 5
  a(data-goto="#6") 6

main
  p(data-id="1") 1
  p(data-id="2") 2
  p(data-id="3") 3
  p(data-id="4") 4
  p(data-id="5") 5
  p(data-id="6") 6

SCSS

html, body {
  width: 100%;
  height: 100%;
  max-height: 100%;
}

main {
  height: 100%;
  max-height: 100%;
  overflow: scroll;
  width: 500px;
}

nav {
  background: red;
  color: white;
  position: fixed;
  width: 50%;
  left: 50%;
}

a {
  color: white;
  cursor: pointer;
  display: block;
  padding: 10px 20px;
  &:hover {
    background: lighten(red, 20%);
  }
}

p {
  width: 400px;
  height: 400px;
  border: solid 2px green;
  padding: 30px;
}

JS

var links = document.querySelectorAll('a'),
    paras = document.querySelectorAll('p'),
    main  = document.querySelector('main');

for (var i = 0; i < links.length; i++) {
  links[i].addEventListener('click', function(){
    var linkID = this.getAttribute('data-goto').slice(1);
    for (var j = 0; j < links.length; j++) {
      if(linkID === paras[j].getAttribute('data-id')) {
         window.scrollTo(0, paras[j].offsetTop); 
      }
    }
  })
}

只有PURE JS请 - 没有JQUERY

PURE JS ONLY PLEASE - NO JQUERY

你想要的是设置 < main> 元素上的scrollTop 属性。

What you want is to set the scrollTop property on the <main> element.

var nav = document.querySelector('nav'),
    main  = document.querySelector('main');

  nav.addEventListener('click', function(event){
    var linkID,
      scrollTarget;
    if (event.target.tagName.toUpperCase() === "A") {
      linkID = event.target.dataset.goto.slice(1);
      scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
      main.scrollTop = scrollTarget.offsetTop;
    }
  });

你会注意到我做了其他几件不同的事情:

You'll notice a couple of other things I did different:


  • 我用事件委托因此我只需将一个事件附加到 nav 元素,这将更有效地处理任何链接上的点击。

  • 同样,我没有循环遍历所有 p 元素,而是选择了我想要的属性选择器

  • I used event delegation so I only had to attach one event to the nav element which will more efficiently handle clicks on any of the links.
  • Likewise, instead of looping through all the p elements, I selected the one I wanted using an attribute selector

这不仅更高效,可扩展,而且还可以生成更短,更易于维护的代码。

This is not only more efficient and scalable, it also produces shorter, easier to maintain code.

这段代码只会跳转到元素,对于动画卷轴,你需要编写一个函数逐步更新 scrollTop 之后使用 setTimeout

This code will just jump to the element, for an animated scroll, you would need to write a function that incrementally updates scrollTop after small delays using setTimeout.

var nav = document.querySelector('nav'),
    main  = document.querySelector('main'),
    scrollElementTo = (function () {
      var timerId;
      return function (scrollWithin, scrollTo, pixelsPerSecond) {
        scrollWithin.scrollTop = scrollWithin.scrollTop || 0;
        var pixelsPerTick = pixelsPerSecond / 100,
          destY = scrollTo.offsetTop,
          direction = scrollWithin.scrollTop < destY ? 1 : -1,
          doTick = function () {
            var distLeft = Math.abs(scrollWithin.scrollTop - destY),
              moveBy = Math.min(pixelsPerTick, distLeft);
            scrollWithin.scrollTop += moveBy * direction;
            if (distLeft > 0) {
              timerId = setTimeout(doTick, 10);
            }
          };
        clearTimeout(timerId);
        doTick();
      };
    }());

nav.addEventListener('click', function(event) {
  var linkID,
    scrollTarget;
  if (event.target.tagName.toUpperCase() === "A") {
    linkID = event.target.dataset.goto.slice(1);
    scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
    scrollElementTo(main, scrollTarget, 500);
  }
});

您可能遇到的另一个问题是,如果 a 元素包含子元素并且单击子元素,它将是事件的目标而不是 a 标记本身。您可以使用我写的 getParentAnchor 函数来解决这个问题这里

Another problem you might have with the event delegation is that if the a elements contain child elements and a child element is clicked on, it will be the target of the event instead of the a tag itself. You can work around that with something like the getParentAnchor function I wrote here.