javascript - 在 Vanilla JS 中滚动到下一部分并返回到顶部而无需 ID - 无 JQUERY

标签 javascript jquery ecmascript-6

我使用了修改后的 jQuery 脚本,以便能够滚动浏览没有 ID 的部分,并在到达文档末尾时返回到顶部。

由于我是 JS 新手,并尝试改进和学习,所以我想摆脱 jQuery 依赖。

我已经设法让它部分工作,但失败了,并且在寻找 jQueries Offset 方法的替代方法时感到困惑。

This is the modified jQuery script:

// scoll to next section and back to top
var $scrollSection = $('section');
var $scrollTrigger = $('.section_trigger');
var nextSection = 1;

$scrollTrigger.on('click', function() {
  $(this).removeClass('go-to-top');

  // If reached the last section, scroll back to the top on the next click:
  if (nextSection >= $scrollSection.length) {
    $('html, body').animate({
      scrollTop: 0
    }, 1000);
    nextSection = 0;
    return;
  }

  // if we scroll down increment section counter
  while ($('body').scrollTop() > $($scrollSection[nextSection]).offset().top) {
    nextSection++;
  }

  // If next section is the last, add class to rotate arrow:
  if (nextSection === ($scrollSection.length - 1)) {
    $(this).addClass('go-to-top');
  }

  // Move to next section and increment counter
  $('html, body').animate({
    scrollTop: $($scrollSection[nextSection]).offset().top
  }, 1000);
  nextSection++;
});

// turn arrow when scrolled to footer
$(window).scroll(function() {
  if ($(window).scrollTop() + $(window).height() == $(document).height()) {
    $(".section_trigger--side").addClass('go-to-top');
  } else {
    $(".section_trigger--side").removeClass('go-to-top');
  }
});
.box_0 {
  background-color: grey;
  height: 100vh;
  width: 100vw;
}

.box_1 {
  background-color: green;
  height: 50vh;
  width: 100vw;
}

.box_2 {
  background-color: blue;
  height: 50vh;
  width: 100vw;
}

.box_3 {
  background-color: yellow;
  height: 20vh;
  width: 100vw;
}

footer {
  background-color: pink;
  height: 40vh;
  width: 100vw;
}

/* styling for the section trigger */
.section_trigger svg {
  height: 60%;
  left: 20%;
  position: absolute;
  top: 20%;
  width: 60%;
}

/** @define c-scroll-nav */
.section_trigger {
  background: hsla(0, 0%, 0%, 0.45);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  height: 50px;
  width: 50px;
}

.section_trigger:hover {
  background: black;
}

.section_trigger--center {
  position: absolute;
  left: 50%;
  transform: translate(0, -50%);
  bottom: 12px;
  transform: rotate(270deg);
}

.section_trigger--side {
  bottom: 12px;
  position: fixed;
  right: 12px;
  transform: rotate(270deg);
}

.go-to-top {
  transform: rotate(90deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="section_trigger section_trigger--center">
  <svg viewBox="0 0 100 100"><path d="M 10,50 L 60,100 L 70,90 L 30,50  L 70,10 L 60,0 Z" fill="white" class="arrow"></path></svg>
</span>
<span class="section_trigger section_trigger--side">
  <svg viewBox="0 0 100 100"><path d="M 10,50 L 60,100 L 70,90 L 30,50  L 70,10 L 60,0 Z" fill="white" class="arrow"></path></svg>
</span>
<section>
  <div class="box_0"></div>
</section>
<section>
  <div class="box_2"></div>
</section>
<section>
  <div class="box_3"></div>
</section>
<section>
  <div class="box_1"></div>
</section>
<section>
  <div class="box_2"></div>
</section>
<section>
  <div class="box_3"></div>
</section>
<footer></footer>

This is my Vanilla JS approach:

// Debuging
window.onscroll = function() {
  //console.log('top: '  + (window.pageYOffset || document.documentElement.scrollTop));
  // console.log('section: '  + (window.pageYOffset || document.scrollSection[nextSection].scrollTop));
  // console.log(nextSection);
  // console.log(scrollSection[0]);
}

/*
 * Helper Function: Vanilla version of jQuery scrollTop
 */
function scrollTo(element, to, duration) {
  if (duration <= 0) return;
  let difference = to - element.scrollTop;
  let perTick = difference / duration * 50;

  setTimeout(() => {
    element.scrollTop = element.scrollTop + perTick;
    if (element.scrollTop == to) return;
    scrollTo(element, to, duration - 10);
  }, 10);
}

/*
 * Main Function: Scroll to section elements and back to top
 */
const scrollSection = document.getElementsByTagName('section');
const scrollTrigger = document.getElementsByClassName('section_trigger')[1];
let nextSection = 0;

// turn arrow back arround after clicked to return to top
scrollTrigger.addEventListener('click', () => {
  scrollTrigger.classList.remove('go-to-top');
  console.log('removed class `go-to-top`');

  // If reached the last section, scroll back to the top on the next click:
  if (nextSection >= scrollSection.length) {
    function runScroll() {
      scrollTo(document.body, 0, 1000);
      nextSection = 0;
      console.log('Scrolled back to Top');
    }
  }

  // if we scroll down increment section counter
  while (document.body.scrollTop > document.scrollSection[nextSection].getBoundingClientRect().scrollTop) { //doesnt work , cant next section scrollSection undefined
    nextSection++;
    console.log('incremented counter to' + nextSection);
  }

  // If next section is the last, add go-to-top class to rotate arrow:
  if (nextSection === (scrollSection.length - 1)) {
    scrollTrigger.classList.add('go-to-top');
    console.log('added class `go-to-top`');
  }

  // Move to next section and increment counter
  function runScroll() {
    scrollTo(document.scrollSection[nextSection].getBoundingClientRect().scrollTop, 0, 1000); //doesnt work , cant next section scrollSection undefined
    nextSection++;
    console.log('Scrolled one section down');
  }

});

/*
 * Helper Function: vanilla JS get document height and window width and height
 */
const body = document.body;
const html = document.documentElement;

// document height
const documentHeight = Math.max(body.scrollHeight, body.offsetHeight,
  html.clientHeight, html.scrollHeight, html.offsetHeight);

// window width and height
const windowWidth = window.innerWidth || document.documentElement.clientWidth || body.clientWidth;
const windowHeigth = window.innerHeight || document.documentElement.clientHeight || body.clientHeight;

// turn arrow when scrolled to footer
window.onscroll = function() {
  if (window.getBoundingClientRect() + windowHeigth == documentHeight) { // not sure if getBoundingClientRect is the right choice since its relative to the viewport
    scrollTrigger.classList.add('go-to-top');
  } else {
    scrollTrigger.classList.remove('go-to-top');
  }
};
.box_0 {
  background-color: lightgreen;
  height: 100vh;
  width: 100vw;
}

.box_1 {
  background-color: magenta;
  height: 50vh;
  width: 100vw;
}

.box_2 {
  background-color: blue;
  height: 50vh;
  width: 100vw;
}

.box_3 {
  background-color: yellow;
  height: 20vh;
  width: 100vw;
}

footer {
  background-color: pink;
  height: 40vh;
  width: 100vw;
}

/* styling for the section trigger */
.section_trigger svg {
  height: 60%;
  left: 20%;
  position: absolute;
  top: 20%;
  width: 60%;
}

/** @define c-scroll-nav */
.section_trigger {
  background: hsla(0, 0%, 0%, 0.45);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  height: 50px;
  width: 50px;
}

.section_trigger:hover {
  background: black;
}

.section_trigger--center {
  position: absolute;
  left: 50%;
  transform: translate(0, -50%);
  bottom: 12px;
  transform: rotate(270deg);
}

.section_trigger--side {
  bottom: 12px;
  position: fixed;
  right: 12px;
  transform: rotate(270deg);
}

.go-to-top {
  transform: rotate(90deg);
}
<span class="section_trigger section_trigger--center">
  <svg viewBox="0 0 100 100"><path d="M 10,50 L 60,100 L 70,90 L 30,50  L 70,10 L 60,0 Z" fill="white" class="arrow"></path></svg>
</span>
<span class="section_trigger section_trigger--side go-to-top">
  <svg viewBox="0 0 100 100"><path d="M 10,50 L 60,100 L 70,90 L 30,50  L 70,10 L 60,0 Z" fill="white" class="arrow"></path></svg>
</span>
<section>
  <div class="box_0"></div>
</section>
<section>
  <div class="box_2"></div>
</section>
<section>
  <div class="box_3"></div>
</section>
<section>
  <div class="box_1"></div>
</section>
<section>
  <div class="box_2"></div>
</section>
<section>
  <div class="box_3"></div>
</section>
<footer></footer>

我认为我没有正确使用 getBoundingClientRect() 方法,我也不确定是否应该相对于视口(viewport)使用它。

除此之外,我不知道它是否有效以及我通常采用的正确方法,我还读到可以使用 throttle 来提高性能?方法......非常感谢,非常感谢你们。

最佳答案

抱歉,我没有时间完成,但我已经做了一些更新。单击中心滚动触发器时,该代码不会触发错误,但它也不起作用。我现在发帖是为了保存我的答案,并将根据需要进行更新。

在做这类事情时,我的建议是分段实现。例如,使用有效的 jQuery 代码,然后用纯 JS 一次替换一个函数或一小部分。调试起来会更容易。

一些变化:

  1. if (window.pageYOffset + windowHeight == documentHeight) {
  2. const scrollTrigger = document.getElementsByClassName('section_trigger')[0];

代码:

// Debuging
window.onscroll = function() {
  // console.log('top: '  + (window.pageYOffset || document.documentElement.scrollTop));
  // console.log('section: '  + (window.pageYOffset || document.scrollSection[nextSection].scrollTop));
  // console.log(nextSection);
  // console.log(scrollSection[0]);
}

/*
 * Helper Function: Vanilla version of jQuery scrollTop
 */

function scrollTo(element, to, duration) {
  if (duration <= 0) return;
  let difference = to - element.scrollTop;
  let perTick = difference / duration * 50;

  setTimeout(() => {
    element.scrollTop = element.scrollTop + perTick;
    if (element.scrollTop == to) return;
    scrollTo(element, to, duration - 10);
  }, 10);
}

/*
 * Main Function: Scroll to section elements and back to top
 */

const scrollSection = document.getElementsByTagName('section');
const scrollTrigger = document.getElementsByClassName('section_trigger')[0];
let nextSection = 0;

// turn arrow back arround after clicked to return to top
scrollTrigger.addEventListener('click', () => {
  scrollTrigger.classList.remove('go-to-top');
  console.log('removed class `go-to-top`');

  // If reached the last section, scroll back to the top on the next click:
  if (nextSection >= scrollSection.length) {
    function runScroll() {
      scrollTo(document.body, 0, 1000);
      nextSection = 0;
      console.log('Scrolled back to Top');
    }
  }

  scrollTo(document.body, 0, 1000);

  // if we scroll down increment section counter
  while (document.body.scrollTop > scrollSection[nextSection].getBoundingClientRect().scrollTop) { //doesnt work , cant next section scrollSection undefined
    nextSection++;
    console.log('incremented counter to' + nextSection);
  }

  // If next section is the last, add go-to-top class to rotate arrow:
  if (nextSection === (scrollSection.length - 1)) {
    scrollTrigger.classList.add('go-to-top');
    console.log('added class `go-to-top`');
  }

  // Move to next section and increment counter
  function runScroll() {
    scrollTo(document.scrollSection[nextSection].getBoundingClientRect().scrollTop, 0, 1000); //doesnt work , cant next section scrollSection undefined
    nextSection++;
    console.log('Scrolled one section down');
  }

});

/*
 * Helper Function: vanilla JS get document height and window width and height
 */

const body = document.body;
const html = document.documentElement;

// document height
const documentHeight = Math.max(body.scrollHeight, body.offsetHeight,
  html.clientHeight, html.scrollHeight, html.offsetHeight);

// window width and height
const windowWidth = window.innerWidth || document.documentElement.clientWidth || body.clientWidth;
const windowHeight = window.innerHeight || document.documentElement.clientHeight || body.clientHeight;

// turn arrow when scrolled to footer
window.onscroll = function() {
  if (window.pageYOffset + windowHeight == documentHeight) { // not sure if getBoundingClientRect is the right choice since its relative to the viewport
    scrollTrigger.classList.add('go-to-top');
  } else {
    scrollTrigger.classList.remove('go-to-top');
  }
};
.box_0 {
  background-color: lightgreen;
  height: 100vh;
  width: 100vw;
}

.box_1 {
  background-color: magenta;
  height: 50vh;
  width: 100vw;
}

.box_2 {
  background-color: blue;
  height: 50vh;
  width: 100vw;
}

.box_3 {
  background-color: yellow;
  height: 20vh;
  width: 100vw;
}

footer {
  background-color: pink;
  height: 40vh;
  width: 100vw;
}


/* styling for the section trigger */

.section_trigger svg {
  height: 60%;
  left: 20%;
  position: absolute;
  top: 20%;
  width: 60%;
}


/** @define c-scroll-nav
  */

.section_trigger {
  background: hsla(0, 0%, 0%, 0.45);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  height: 50px;
  width: 50px;
}

.section_trigger:hover {
  background: black;
}

.section_trigger--center {
  position: absolute;
  left: 50%;
  transform: translate(0, -50%);
  bottom: 12px;
  transform: rotate(270deg);
}

.section_trigger--side {
  bottom: 12px;
  position: fixed;
  right: 12px;
  transform: rotate(270deg);
}

.go-to-top {
  transform: rotate(90deg);
}
<span class="section_trigger section_trigger--center">
  <svg viewBox="0 0 100 100"><path d="M 10,50 L 60,100 L 70,90 L 30,50  L 70,10 L 60,0 Z" fill="white" class="arrow"></path></svg>
</span>
<span class="section_trigger section_trigger--side go-to-top">
  <svg viewBox="0 0 100 100"><path d="M 10,50 L 60,100 L 70,90 L 30,50  L 70,10 L 60,0 Z" fill="white" class="arrow"></path></svg>
</span>
<section>
  <div class="box_0"></div>
</section>
<section>
  <div class="box_2"></div>
</section>
<section>
  <div class="box_3"></div>
</section>
<section>
  <div class="box_1"></div>
</section>
<section>
  <div class="box_2"></div>
</section>
<section>
  <div class="box_3"></div>
</section>
<footer></footer>

关于javascript - 在 Vanilla JS 中滚动到下一部分并返回到顶部而无需 ID - 无 JQUERY,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42406287/

相关文章:

javascript - 将随机选择的值显示到类内的段落标签

javascript - 暂停一个简单的 slider 操作 |设置时间间隔

javascript - jQuery 选择器规范不一致

javascript - 将旧数组中的新键名称分配给数组

javascript - 我的 react-native 项目不是 Debug模式

javascript - 根据用户输入创建表单字段

javascript - <Textarea> 定位在 <ul> TreeView 中

javascript - 基于浏览器插件的 RIA 与基于 Java Script 的 RIA 的性能

javascript - 使用 jQuery 计算购物车总数

javascript - 总结对象数组的频率