最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在React項目中實現一個簡單的錨點目錄定位

 更新時間:2023年09月26日 09:27:51   作者:linwu  
錨點目錄定位功能在長頁面和文檔類網站中非常常見,它可以讓用戶快速定位到頁面中的某個章節(jié),本文講給大家介紹一下React項目中如何實現一個簡單的錨點目錄定位,文中有詳細的實現代碼,需要的朋友可以參考下

前言

錨點目錄定位功能在長頁面和文檔類網站中非常常見,它可以讓用戶快速定位到頁面中的某個章節(jié)

  • 如何在React中實現錨點定位和平滑滾動
  • 目錄自動高亮的實現思路
  • 處理頂部導航遮擋錨點的解決方案
  • 服務端渲染下的實現方案
  • 性能優(yōu)化策略

實現基本錨點定位

首先,我們需要實現頁面內基本的錨點定位功能。對于錨點定位來說,主要涉及這兩個部分:

  • 設置錨點,為頁面中的某個組件添加id屬性
  • 點擊鏈接,跳轉到指定錨點處

例如:

// 錨點組件
function AnchorComponent() {
  return <h2 id="anchor">This is anchor</h2> 
}
// 鏈接組件
function LinkComponent() {
  return (
    <a href="#anchor" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >Jump to Anchor</a> 
  )
}

當我們點擊Jump to Anchor這個鏈接時,頁面會平滑滾動到AnchorComponent所在的位置。

使用useScrollIntoView自定義hook

React中實現錨點定位,最簡單的方式就是使用useScrollIntoView這個自定義hook。

import { useScrollIntoView } from 'react-use';
function App() {
  const anchorRef = useRef();  
  const scrollToAnchor = () => {
    useScrollIntoView(anchorRef);
  }
  return (
    <>
      <a href="#anchor" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  onClick={scrollToAnchor}>
        Jump to Anchor  
      </a>
      <h2 id="anchor" ref={anchorRef}>This is anchor</h2>
    </>
  )
}

useScrollIntoView接受一個ref對象,當調用這個hook函數時,會自動滾動頁面,使得ref對象在可視區(qū)域內。

原生scrollIntoView方法

useScrollIntoView內部其實就是使用了原生的scrollIntoView方法,所以我們也可以直接調用:

function App() {
  const anchorRef = useRef();
  const scrollToAnchor = () => {
    anchorRef.current.scrollIntoView({
      behavior: 'smooth',
      block: 'start'
    })
  };
  return (
    <>  
      <a href="#anchor" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  onClick={scrollToAnchor}>Jump to Anchor</a>
      <h2 id="anchor" ref={anchorRef}>This is anchor</h2> 
    </>
  )
}

scrollIntoView可以讓元素的父容器自動滾動,將這個元素滾動到可見區(qū)域。behavior:'smooth'可以啟用平滑滾動效果。

錨點定位和目錄聯動

很多時候,我們會在頁面中實現一個目錄導航,可以快速定位到各個章節(jié)。此時就需要實現錨點定位和目錄的聯動效果:

  • 點擊目錄時,自動滾動到對應的章節(jié)
  • 滾動頁面時,自動高亮正在瀏覽的章節(jié)

目錄導航組件

目錄導航本身是一個靜態(tài)組件,我們通過props傳入章節(jié)數據:

function Nav({ chapters }) {
  return (
    <ul className=" chapters">
      {chapters.map(chapter => (
        <li key={chapter.id}>
          <a href={'#' + chapter.id}>  
            {chapter.title}
          </a>
        </li>
      ))}
    </ul>
  )
}

錨點組件

然后在頁面中的每一章使用Anchor組件包裹:

function Chapter({ chapter }) {
  return (
    <Anchor id={chapter.id}>  
      <h2>{chapter.title}</h2>
      {chapter.content}
    </Anchor>
  )
}
function Anchor({ children, id }) {
  return (
    <div id={id}>
      {children}  
    </div>
  )
}

這樣通過id屬性建立章節(jié)內容和目錄鏈接之間的關聯。

處理點擊事件

當點擊目錄鏈接時,需要滾動到對應的章節(jié)位置:

function App() {
  //...
  const scrollToChapter = (chapterId) => {
    const chapterEl = document.getElementById(chapterId);
    chapterEl.scrollIntoView({ behavior: 'smooth' });
  }
  return (
    <>
      <Nav 
        chapters={chapters}
        onLinkClick={(chapterId) => scrollToChapter(chapterId)} 
      />
      {chapters.map(chapter => (
        <Chapter 
         key={chapter.id}
         chapter={chapter}
        />
      ))}
    </>
  )
}

給Nav組件傳一個onLinkClick回調,當點擊鏈接時,通過chapterId獲取到元素,并滾動到可視區(qū)域,實現平滑跳轉。

自動高亮

實現自動高亮也很簡單,通過監(jiān)聽滾動事件,計算章節(jié)元素的偏移量,判斷哪個章節(jié)在可視區(qū)域內,并更新active狀態(tài):

function App() {
  const [activeChapter, setActiveChapter] = useState();
  useEffect(() => {
    const handleScroll = () => {
      chapters.forEach(chapter => {
        const element = document.getElementById(chapter.id);
        // 獲取元素在可視區(qū)域中的位置
        const rect = element.getBoundingClientRect();  
        // 判斷是否在可視區(qū)域內 
        if (rect.top >= 0 && rect.bottom <= window.innerHeight) {
          setActiveChapter(chapter.id);
        }
      })
    }
    window.addEventListener('scroll', handleScroll);
    return () => {
      window.removeEventListener('scroll', handleScroll);
    }
  }, []);
  return (
    <>
     <Nav
       chapters={chapters}
       activeChapter={activeChapter}
      />
    </>
  )
}

通過getBoundingClientRect可以得到元素相對于視窗的位置信息,根據位置判斷是否在可見區(qū)域內,如果是就更新activeChapter狀態(tài),從而觸發(fā)目錄的高亮效果。

問題解析

遮擋問題

有時錨點會被固定的Header遮擋,此時滾動會定位到元素上方,用戶看不到錨點對應的內容。

常見的解決方案是:

  • 設置錨點元素margin-top
#anchor {
  margin-top: 80px; /* header高度 */
}

直接設置一個和Header高度相同的margin,來防止遮擋。

  • 在滾動方法中加入offset
// scroll offset
const scrollOffset = -80; 
chapterEl.scrollIntoView({
  offsetTop: scrollOffset
})

給scrollIntoView傳入一個頂部偏移量,這樣也可以跳過Header的遮擋。

響應式問題

在響應式場景下,目錄的遮擋問題會更復雜。我們需要區(qū)分不同斷點下,計算匹配的offset。

可以通過MatchMedia Hook獲取當前的斷點:

import { useMediaQuery } from 'react-responsive';
function App() {
  const isMobile = useMediaQuery({ maxWidth: 767 });
  const isTablet = useMediaQuery({ minWidth: 768, maxWidth: 1023 });
  const isDesktop = useMediaQuery({ minWidth: 1024 });
  let scrollOffset = 0;
  if (isMobile) {
    scrollOffset = 46; 
  } else if (isTablet) {  
    scrollOffset = 60;
  } else if (isDesktop) {
    scrollOffset = 80;
  }
  const scrollToChapter = (chapterId) => {
    const chapterEl = document.getElementById(chapterId);
    chapterEl.scrollIntoView({
      offsetTop: scrollOffset  
    })
  }
  //...
}

根據不同斷點,動態(tài)計算滾動偏移量,這樣可以適配所有情況。

性能優(yōu)化

使用節(jié)流

滾動事件會高頻觸發(fā),直接在滾動回調中計算章節(jié)位置會造成性能問題。

我們可以使用Lodash的throttle函數進行節(jié)流:

import throttle from 'lodash.throttle';
const handleScroll = throttle(() => {
  // 計算章節(jié)位置
}, 100);

這樣可以限制滾動事件最多每100ms觸發(fā)一次。

IntersectionObserver

使用IntersectionObserver提供的異步回調,只在章節(jié)進入或者離開可視區(qū)域時才執(zhí)行位置計算:

import { useRef, useEffect } from 'react';
function App() {
  const chaptersRef = useRef({});
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        // 章節(jié)進入或者離開可視區(qū)域時更新
      }
    );
    chapters.forEach(chapter => {
      observer.observe(
        document.getElementById(chapter.id)  
      );
    })
  }, []);
} 

這種懶加載式的方式可以大幅減少無效的位置計算。

SSR支持

在Next.js等SSR場景下,客戶端腳本會延后加載,頁面初次渲染時目錄聯動會失效。

getInitialProps注水

可以在getInitialProps中提前計算目錄數據,注入到頁面中:

Home.getInitialProps = async () => {
  const chapters = await fetchChapters();
  const mappedChapters = chapters.map(chapter => {
    return {
      ...chapter,
      highlighted: isChapterHighlighted(chapter) 
    }
  });
  return {
    chapters: mappedChapters
  };
};

hydrate處理

客戶端腳本加載后,需要調用ReactDOM.hydrate而不是render方法,進行數據的補充填充,避免目錄狀態(tài)丟失。

import { useEffect } from 'react';
function App({ chapters }) {
  useEffect(() => {
    ReactDOM.hydrate(
      <App chapters={chapters} />,  
      document.getElementById('root')
    );
  }, []);
}

服務端渲染的實現方案

在使用了服務端渲染(SSR)的框架如Next.js等情況下,實現錨點定位和目錄聯動也會有一些不同。

主要區(qū)別在于:

  • 服務端和客戶端環(huán)境不統(tǒng)一
  • 腳本加載時間差

這會導致一些狀態(tài)錯位的問題。

問題復現

假設我們有下面的目錄和內容結構:

function Nav({ chapters }) {
  return (
    <ul>
      {chapters.map(ch => (
        <li>
          <a href={'#' + ch.id}>{ch.title}</a>
        </li>
      ))}
    </ul>
  )
}
function Chapter({ chapter }) {
  const ref = useRef();
  // 占位組件
  return <div ref={ref}>{chapter.content}</div> 
}
function App() {
  const chapters = [
    { id: 'chapter-1', title: 'Chapter 1' },
    { id: 'chapter-2', title: 'Chapter 2' },
  ];
  return (
    <>
      <Nav chapters={chapters} />
      <Chapter chapter={chapters[0]} />
      <Chapter chapter={chapters[1]} />
    </>
  )
}

非SSR環(huán)境下,點擊鏈接和滾動都可以正常工作。

但是在Next.js的SSR環(huán)境下就會有問題:

點擊目錄鏈接時,頁面不會滾動。

這是因為在服務端,我們無法獲取組件的ref,所以錨點元素不存在,自然無法定位。

滾動頁面時,目錄高亮也失效。

服務端渲染的靜態(tài)HTML中,并沒有綁定滾動事件,所以無法自動高亮。

預取數據

首先,我們需要解決點擊目錄鏈接的問題。

既然服務端無法獲取組件ref,那就需要在客戶端去獲取元素位置。

這里有兩個方法:

  • 組件掛載后主動緩存元素位置
// Chapter組件
useEffect(() => {
  // 緩存位置數據
  cacheElementPosition(chapter.id, ref.current); 
}, []);
// Utils
const elementPositions = {};
function cacheElementPosition(id, element) {
  const rect = element.getBoundingClientRect();
  elementPositions[id] = {
    left: rect.left,
    top: rect.top,
  }
}
  • 點擊時實時獲取元素位置
// handle link click
const scrollToChapter = (chapterId) => {
  const element = document.getElementById(chapterId);
  const rect = element.getBoundingClientRect();
  window.scrollTo({
    top: rect.top,
    behavior: 'smooth'
  })
}

無論哪種方法,都需要在組件掛載后獲取元素的位置信息。

這樣我們就可以在點擊目錄鏈接時,正確滾動到對應的章節(jié)位置了。

數據注水

但是點擊目錄只解決了一半問題,滾動高亮還需要解決。

這里就需要用到數據注水的技術。

簡單來說就是:

  • 在服務端渲染時,讀取路由參數,提前計算高亮狀態(tài)
  • 將高亮數據注入到響應中
  • 客戶端拿到注水的數據后渲染,不會出現高亮錯位

實現步驟:

1.服務端獲取參數和數據

// 在getServerSideProps中
export async function getServerSideProps(context) {
  const { hashtag } = context.query;
  const chapters = await fetchChapters();
  const highlightedChapter = chapters.find(ch => ch.id === hashtag);
  return {
    props: {
      chapters,
      highlightedChapter  
    }
  }
}

2.客戶端讀取props

function Nav({ chapters, highlightedChapter }) {
  return (
    <ul>
      {chapters.map(ch => (
        <li className={ch.id === highlightedChapter?.id ? 'highlighted' : ''}>
        </li>
      ))}
    </

以上就是在React項目中實現一個簡單的錨點目錄定位的詳細內容,更多關于React實現錨點目錄定位的資料請關注腳本之家其它相關文章!

相關文章

  • react優(yōu)雅處理多條件鼠標拖拽位移

    react優(yōu)雅處理多條件鼠標拖拽位移

    這篇文章主要為大家詳細介紹了react優(yōu)雅處理多條件鼠標拖拽位移,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • react的ui庫antd中form表單使用SelectTree反顯問題及解決

    react的ui庫antd中form表單使用SelectTree反顯問題及解決

    這篇文章主要介紹了react的ui庫antd中form表單使用SelectTree反顯問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • react hooks實現防抖節(jié)流的方法小結

    react hooks實現防抖節(jié)流的方法小結

    這篇文章主要介紹了react hooks實現防抖節(jié)流的幾種方法,文中通過代碼示例給大家講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-04-04
  • 詳解Redux的工作流程

    詳解Redux的工作流程

    這篇文章主要介紹了Redux的工作流程,redux是一個專門用于做狀態(tài)管理的JS庫,它可以在react、angular、vue等項目中,但基本與react配合使用,需要的朋友可以參考下
    2022-08-08
  • React Refs轉發(fā)實現流程詳解

    React Refs轉發(fā)實現流程詳解

    Refs是一個 獲取 DOM節(jié)點或React元素實例的工具,在React中Refs 提供了一種方式,允許用戶訪問DOM 節(jié)點或者在render方法中創(chuàng)建的React元素,這篇文章主要給大家介紹了關于React中refs的一些常見用法,需要的朋友可以參考下
    2022-12-12
  • React實現隨機顏色選擇器的示例代碼

    React實現隨機顏色選擇器的示例代碼

    顏色選擇器是一個用于選擇和調整顏色的工具,它可以讓用戶選擇他們喜歡的顏色,本文主要介紹了React實現隨機顏色選擇器的示例代碼,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • React中使用Echarts無法顯示title、tooltip等組件的解決方案

    React中使用Echarts無法顯示title、tooltip等組件的解決方案

    這篇文章主要介紹了React中使用Echarts無法顯示title、tooltip等組件的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • react-router-dom 嵌套路由的實現

    react-router-dom 嵌套路由的實現

    這篇文章主要介紹了react-router-dom 嵌套路由的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • JavaScript React如何修改默認端口號方法詳解

    JavaScript React如何修改默認端口號方法詳解

    這篇文章主要介紹了JavaScript React如何修改默認端口號方法詳解,文中通過步驟圖片解析介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • React組件實例三大核心屬性State props Refs詳解

    React組件實例三大核心屬性State props Refs詳解

    組件實例的三大核心屬性是:State、Props、Refs。類組件中這三大屬性都存在。函數式組件中訪問不到 this,也就不存在組件實例這種說法,但由于它的特殊性(函數可以接收參數),所以存在Props這種屬性
    2022-12-12

最新評論

历史| 锦州市| 潍坊市| 临沂市| 曲沃县| 西乌珠穆沁旗| 六枝特区| 新巴尔虎左旗| 天峨县| 樟树市| 诸暨市| 常德市| 米易县| 乡城县| 荔浦县| 新宁县| 舒城县| 平塘县| 轮台县| 静安区| 宝丰县| 盐亭县| 西青区| 竹山县| 微山县| 双桥区| 常德市| 夏河县| 织金县| 南部县| 通榆县| 修武县| 临夏县| 清新县| 拜泉县| 区。| 林甸县| 犍为县| 乾安县| 嵊州市| 开鲁县|