React tooltip的封裝

前端頁面中的tooltip一直是被廣泛應用的功能,tooltip本身的dom結構萬變不離其宗,最大的問題可能就是那個氣泡框下面的三角形的渲染,但是對於老手來說,解決這種小問題和喫飯喝水一樣簡單。

// ant中tooltip的使用方法
import { Tooltip, Button } from 'antd';

ReactDOM.render(
  <div>
    <Tooltip placement="topLeft" title="Prompt Text">
      <Button>Align edge / 邊緣對齊</Button>
    </Tooltip>
    <Tooltip placement="topLeft" title="Prompt Text" arrowPointAtCenter>
      <Button>Arrow points to center / 箭頭指向中心</Button>
    </Tooltip>
  </div>,
  mountNode,
);

自從我開始使用react和antd之後,我一直很好奇antd裏面的tooltip是怎麼封裝出來的,後來的工作中因爲機緣巧合的關係,一直都沒時間仔細研究一下,今天稍微花了點時間,實現和antd裏面差不多的使用效果。

其實實現tooltip並不是什麼很困難的事情,困難的是如何將其優雅地封裝起來,如同ant中的一樣,使用Tooltip模塊將需要觸發的實體包裹起來,對於使用者來說是最直觀,最方便的使用方法。而且ant的tooltip的實現中,並不會侵入頁面的原始結構。

技術難點:

1. 觸發事件的注入

想要在react中實現,不入侵頁面的結構的tooltip,就必須要將觸發事件直接注入到props.children中,而不是在外面再包一層dom,再包一層dom會影響到css樣式的編寫。所以需要用到Rreact.cloneElement的Api

return (
    <>
      {React.Children.map(props.children, (child: any) => {
        return React.cloneElement(child, {
          className: child.props.className + ' tooltip-wrap-content',
          onMouseEnter: mouseIn,
          onMouseLeave: mouseOut
        });
      })}
    </>
  );

這個api可以在渲染子節點的時候主動注入自己想要傳遞給子節點的參數,這樣就可以將觸發事件注入進去了。當然不能直接覆蓋子節點的事件,需要做一下代理,這裏是簡單寫了。

2. tooltip的結構

這個其實是小問題,tooltip的結構可以分爲兩個問題,一個是tooltip應該渲染到哪裏,一個是tooltip本身的dom結構的渲染。

在React v16中有一個createPortal的Api,可以將react節點渲染至任意dom(包括react節點)下,用這個方法的話,我們就可以避免將tooltip渲染到實體的附近了,能最大程度避免污染到原始結構。

由於採用了觸發實體與tooltip渲染位置分離的結構,所以就不能直接用絕對定位來確定tooltip在窗口裏的position了,需要使用fixed的定位方式。同時計算觸發實體距離窗口邊界的距離,直接根據窗口定位。雖然不是很有意義,但是還是將如何計算實體距離窗口邊界代碼貼出來好了,畢竟也曾經困擾過我一段時間:

function getOffset(dom) {
    let parent: any = dom;
    let left: number = 0;
    let top: number = 0;
    while (parent) {
      left += parent.offsetLeft;
      top += parent.offsetTop;
      parent = parent.offsetParent;
    }
    return {
      left, top, 
      width: dom.offsetWidth,
      height: dom.offsetHeight
    };
  }

只要搞清楚offsetParent,offsetLeft,offsetTop,offsetWidth,offsetHeight代表的意義就不難看懂上面的代碼了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章