欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS實(shí)現(xiàn)頁(yè)面導(dǎo)航與內(nèi)容相互錨定實(shí)例詳解

 更新時(shí)間:2023年10月20日 08:39:04   作者:Qing  
這篇文章主要為大家介紹了JS實(shí)現(xiàn)頁(yè)面導(dǎo)航與內(nèi)容相互錨定實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引文

在日常的學(xué)習(xí)和工作中,經(jīng)常會(huì)瀏覽的這樣一種網(wǎng)頁(yè),它的結(jié)構(gòu)為左側(cè)是側(cè)邊欄,右側(cè)是內(nèi)容區(qū)域,當(dāng)點(diǎn)擊左側(cè)的側(cè)邊欄上的目錄時(shí),右側(cè)的內(nèi)容區(qū)域會(huì)自動(dòng)滾動(dòng)到該目錄所對(duì)應(yīng)的內(nèi)容區(qū)域;當(dāng)滾動(dòng)內(nèi)容區(qū)域時(shí),側(cè)邊欄上對(duì)應(yīng)的目錄也會(huì)高亮。

恰巧最近需要寫(xiě)個(gè)類(lèi)似的小玩意,簡(jiǎn)單的做下筆記,為了避免有人只熟悉Vue或React框架中的一個(gè)框架,還是使用原生JS來(lái)進(jìn)行實(shí)現(xiàn)。

思路

點(diǎn)擊側(cè)邊欄上的目錄時(shí),通過(guò)獲取點(diǎn)擊的目錄的類(lèi)名、或id、或index,用這些信息作為標(biāo)記,然后在內(nèi)容區(qū)域查找對(duì)應(yīng)的內(nèi)容。

滾動(dòng)內(nèi)容區(qū)域時(shí),根據(jù)內(nèi)容區(qū)域的內(nèi)容的dom節(jié)點(diǎn)獲取標(biāo)記,根據(jù)標(biāo)記來(lái)查找目錄。

實(shí)現(xiàn)

頁(yè)面初始化

首先把html和css寫(xiě)成左邊為目錄,右邊為內(nèi)容的頁(yè)面結(jié)構(gòu),為測(cè)試提供ui界面。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>目錄與內(nèi)容相互錨定</title>
  <style>
    .container {
      display: flex;
      flex-direction: row;
    }
    #nav {
      width: 150px;
      height: 400px;
      background-color: #eee;
    }
    #nav .nav-item {
      cursor: pointer;
    }
    #nav .nav-item.active {
      font-weight: bold;
      background-color: #f60;
    }
    #content {
      flex: 1;
      margin-left: 10px;
      position: relative;
      width: 300px;
      height: 400px;
      overflow-y: scroll;
    }
    .content-block {
      margin-top: 25px;
      height: 200px;
      background-color: #eee;
    }
    .content-block:first-child {
      margin-top: 0;
    }
  </style>
</head>
<body>
  <div class="container">
    <div id="nav">
      <div class="nav-item">目錄 1</div>
      <div class="nav-item">目錄 2</div>
      <div class="nav-item">目錄 3</div>
      <div class="nav-item">目錄 4</div>
      <div class="nav-item">目錄 5</div>
      <div class="nav-item">目錄 6</div>
    </div>
    <div id="content">
      <div class="content-block">內(nèi)容 1</div>
      <div class="content-block">內(nèi)容 2</div>
      <div class="content-block">內(nèi)容 3</div>
      <div class="content-block">內(nèi)容 4</div>
      <div class="content-block">內(nèi)容 5</div>
      <div class="content-block">內(nèi)容 6</div>
    </div>
  </div>
</body>
</html>

通過(guò)點(diǎn)擊實(shí)現(xiàn)內(nèi)容的滾動(dòng)

const nav = document.querySelector("#nav");
const navItems = document.querySelectorAll(".nav-item");
navItems[0].classList.add("active");

nav.addEventListener('click', e => {
navItems.forEach((item, index) => {
  navItems[index].classList.remove("active");
  if (e.target === item) {
    navItems[index].classList.add("active");
    content.scrollTo({
      top: contentBlocks[index].offsetTop,
    });
  }
});
})

通過(guò)滾動(dòng)內(nèi)容實(shí)現(xiàn)導(dǎo)航的高亮

const content = document.querySelector("#content");
const contentBlocks = document.querySelectorAll(".content-block");
let currentBlockIndex = 0;

const handleScroll = function () {
for (let i = 0; i < contentBlocks.length; i++) {
  const block = contentBlocks[i];
  if (
    block.offsetTop <= content.scrollTop &&
    block.offsetTop + block.offsetHeight > content.scrollTop
  ) {
    currentBlockIndex = i;
    break;
  }
}
for (let i = 0; i < navItems.length; i++) {
  const item = navItems[i];
  item.classList.remove("active");
}
navItems[currentBlockIndex].classList.add("active");
};

content.addEventListener("scroll", handleScroll);

最后實(shí)際效果如下

現(xiàn)在能基本實(shí)現(xiàn)點(diǎn)擊左側(cè)的導(dǎo)航來(lái)使右側(cè)內(nèi)容滾動(dòng)到指定區(qū)域,這樣完全可行,但是如果需要平滑滾動(dòng)的話(huà),該怎么來(lái)實(shí)現(xiàn)?

scrollTo這個(gè)函數(shù)提供了滾動(dòng)方式的選項(xiàng)設(shè)置,指定滾動(dòng)方式為平滑滾動(dòng)方式,就可以實(shí)現(xiàn)。

content.scrollTo({
  top: contentBlocks[index].offsetTop,
  behavior: 'smooth
});

來(lái)看下效果

發(fā)現(xiàn)頁(yè)面的滾動(dòng)確實(shí)變得平滑了,但是在點(diǎn)擊左側(cè)的目錄后會(huì)發(fā)生抖動(dòng)的情況,那么為什么會(huì)發(fā)生這樣的情況?

首先觀(guān)察下現(xiàn)象,在點(diǎn)擊目錄5后,目錄5會(huì)在短暫高亮后,然后目錄1開(kāi)始高亮直到目錄5。能夠改變高亮目錄的出了我們點(diǎn)擊的時(shí)候會(huì)讓目錄高亮,另外一個(gè)會(huì)使目錄高亮的地方就是在滾動(dòng)事件函數(shù)里會(huì)根據(jù)內(nèi)容所在位置來(lái)讓目錄高亮。

// content.addEventListener("scroll", handleScroll);

那么我們把對(duì)滾動(dòng)事件的監(jiān)聽(tīng)給去掉后,我們可以看看測(cè)試結(jié)果。

那么現(xiàn)在問(wèn)題確定了,就是在滾動(dòng)過(guò)程中會(huì)影響目錄導(dǎo)航的高亮,所以在剛開(kāi)始滾動(dòng)的時(shí)候會(huì)首先高亮目錄1,那么怎么解決?

比較直接的想法就是我在點(diǎn)擊目錄后,內(nèi)容區(qū)域在滾動(dòng)到對(duì)應(yīng)內(nèi)容區(qū)域時(shí)這段時(shí)間不觸發(fā)滾動(dòng)事件,自然也不會(huì)反過(guò)來(lái)錨定目錄了,但是scrollTo引起內(nèi)容區(qū)域的滾動(dòng)是平滑滾動(dòng),需要一段時(shí)間滾動(dòng)才能結(jié)束,但怎么判斷滾動(dòng)已經(jīng)結(jié)束了呢?

這里我給出自己的思路,就是判斷內(nèi)容區(qū)域的scrollTop是否還在變化,如果沒(méi)有變化了,那么就認(rèn)為滾動(dòng)過(guò)程已經(jīng)結(jié)束了。

let timerId = null;
nav.addEventListener("click", (e) => {
  if (timerId) {
    window.clearInterval(timerId);
  }
  content.removeEventListener("scroll", handleScroll);
  let lastScrollPosition = content.scrollTop;
  timerId = window.setInterval(() => {
    const currentScrollPosition = content.scrollTop;
    console.log(currentScrollPosition, lastScrollPosition);
    if (lastScrollPosition === currentScrollPosition) {
      content.addEventListener("scroll", handleScroll); // 滾動(dòng)結(jié)束后,記得把滾動(dòng)事件函數(shù)重新綁定到scroll事件上去
      window.clearInterval(timerId);
    }
    lastScrollPosition = currentScrollPosition;
  }, 150);
  navItems.forEach((item, index) => {
    navItems[index].classList.remove("active");
    if (e.target === item) {
      navItems[index].classList.add("active");
      content.scrollTo({
        top: contentBlocks[index].offsetTop,
        behavior: "smooth",
      });
    }
  });
});

看看效果

總結(jié)

目前功能已經(jīng)實(shí)現(xiàn),下面把完整的代碼貼出來(lái)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>目錄與內(nèi)容相互錨定</title>
    <style>
      .container {
        display: flex;
        flex-direction: row;
      }
      #nav {
        width: 150px;
        height: 400px;
        background-color: #eee;
      }
      #nav .nav-item {
        cursor: pointer;
      }
      #nav .nav-item.active {
        font-weight: bold;
        background-color: #f60;
      }
      #content {
        flex: 1;
        margin-left: 10px;
        position: relative;
        width: 300px;
        height: 400px;
        overflow-y: scroll;
      }
      .content-block {
        margin-top: 25px;
        height: 200px;
        background-color: #eee;
      }

      .content-block:first-child {
        margin-top: 0;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div id="nav">
        <div class="nav-item">目錄 1</div>
        <div class="nav-item">目錄 2</div>
        <div class="nav-item">目錄 3</div>
        <div class="nav-item">目錄 4</div>
        <div class="nav-item">目錄 5</div>
        <div class="nav-item">目錄 6</div>
      </div>
      <div id="content">
        <div class="content-block">內(nèi)容 1</div>
        <div class="content-block">內(nèi)容 2</div>
        <div class="content-block">內(nèi)容 3</div>
        <div class="content-block">內(nèi)容 4</div>
        <div class="content-block">內(nèi)容 5</div>
        <div class="content-block">內(nèi)容 6</div>
      </div>
    </div>
    <script>
      const content = document.querySelector("#content");
      const contentBlocks = document.querySelectorAll(".content-block");
      const navItems = document.querySelectorAll(".nav-item");
      const nav = document.querySelector("#nav");

      let timerId = null;
      let currentBlockIndex = 0;
      navItems[currentBlockIndex].classList.add("active");

      const handleScroll = function () {
        for (let i = 0; i < contentBlocks.length; i++) {
          const block = contentBlocks[i];
          if (
            block.offsetTop <= content.scrollTop &&
            block.offsetTop + block.offsetHeight > content.scrollTop
          ) {
            currentBlockIndex = i;
            break;
          }
        }
        for (let i = 0; i < navItems.length; i++) {
          const item = navItems[i];
          item.classList.remove("active");
        }
        navItems[currentBlockIndex].classList.add("active");
      };

      nav.addEventListener("click", (e) => {
        if (timerId) {
          window.clearInterval(timerId);
        }
        content.removeEventListener("scroll", handleScroll);
        let lastScrollPosition = content.scrollTop;

        timerId = window.setInterval(() => {
          const currentScrollPosition = content.scrollTop;
          console.log(currentScrollPosition, lastScrollPosition);
          if (lastScrollPosition === currentScrollPosition) {
            content.addEventListener("scroll", handleScroll);
            window.clearInterval(timerId);
          }
          lastScrollPosition = currentScrollPosition;
        }, 150);

        navItems.forEach((item, index) => {
          navItems[index].classList.remove("active");
          if (e.target === item) {
            navItems[index].classList.add("active");
            content.scrollTo({
              top: contentBlocks[index].offsetTop,
              behavior: "smooth",
            });
          }
        });
      });

      content.addEventListener("scroll", handleScroll);
    </script>
  </body>
</html>

以上就是JS實(shí)現(xiàn)頁(yè)面導(dǎo)航與內(nèi)容相互錨定的詳細(xì)內(nèi)容,更多關(guān)于JS實(shí)現(xiàn)頁(yè)面導(dǎo)航與內(nèi)容相互錨定的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論