国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

怎樣給一個(gè)Vue頁面添加大綱導(dǎo)航

susheng / 1181人閱讀

摘要:這里用到一個(gè)非常重要的函數(shù),它會根據(jù)調(diào)用的根節(jié)點(diǎn)遍歷該節(jié)點(diǎn)的子樹,返回符合某個(gè)選擇器的一個(gè)類數(shù)組的對象,但不是數(shù)組,而且遍歷方式就是上文所述的深度優(yōu)先先序遍歷真是激動人心接下來我們可以用這個(gè)元素獲取所有需要導(dǎo)航的元素列表。

一、前言

前兩天項(xiàng)目遇到一個(gè)需要給頁面添加大綱導(dǎo)航的功能,要求把頁面中的特定標(biāo)簽加入到大綱導(dǎo)航中。類似這樣:

需求本身并不難,不過想把這個(gè)東西做得通用一些,也就是以后再有別的頁面需要加導(dǎo)航,不用再重新寫很復(fù)雜的邏輯了。下面說一下具體實(shí)現(xiàn)思路,并且文末會給出簡便易用的導(dǎo)航生成工具。

二、實(shí)現(xiàn)思路 1、需求分析

做之前想到之前接觸過的markdown編輯器mavon-editor有一個(gè)導(dǎo)航,不過那個(gè)導(dǎo)航只能用于編輯器自身,我去看了一下它的表現(xiàn):

點(diǎn)擊右邊的導(dǎo)航節(jié)點(diǎn),會自動定位到對應(yīng)標(biāo)題元素。當(dāng)時(shí)思考了一下它是怎么記錄標(biāo)題元素的,會不會是給標(biāo)題元素加了一個(gè)什么id之類的屬性?于是我看了一下生成的DOM:


竟然是給標(biāo)題元素加了一個(gè)帶有id屬性的a標(biāo)簽的子節(jié)點(diǎn)。不過它生成id的方式比較簡單,單純的"字符串_編號"而已,想來并不是那么可靠(難于保證編輯器外有相同id的元素)。

我大體有了一個(gè)基本的思路:

既然是對于任意頁面都可用,那可以遍歷DOM樹,尋找需要導(dǎo)航的標(biāo)簽,然后把相關(guān)節(jié)點(diǎn)位置信息存儲起來。這里也可一類似mavon-editor給dom樹中插入一個(gè)元素作為一個(gè)錨點(diǎn)。遍歷DOM樹的方法應(yīng)該與DOM渲染后從上到下的順序一致,即采用深度優(yōu)先的先序遍歷方法(先序遍歷即先檢查根元素,再檢查子元素;后序遍歷則相反;如果是二叉樹,還有中序遍歷)。

在所有頁面中,并不能單純根據(jù)h1,h2等標(biāo)簽名來判別一個(gè)元素是否要導(dǎo)航,所以想到了用選擇器來確定,同時(shí)添加根據(jù)選擇器來排除一些例外的元素。

最終的導(dǎo)航應(yīng)該是一個(gè)樹形結(jié)構(gòu),并且每一個(gè)節(jié)點(diǎn)對應(yīng)一個(gè)插入的錨點(diǎn),即每一個(gè)樹節(jié)點(diǎn)應(yīng)該包含一個(gè)錨點(diǎn)信息。

2.實(shí)現(xiàn)思路

因?yàn)轫?xiàng)目是采用Vue來實(shí)現(xiàn),數(shù)據(jù)控制視圖,所以通常不需要直接操作DOM。但是這里需要在DOM中插入錨點(diǎn),Vue自定義指令是一個(gè)不錯(cuò)的選擇。于是可以寫一個(gè)指令,通過需求分析,大體確定可以這個(gè)指令值可以綁定的一個(gè)包含以下三個(gè)信息的對象:

一個(gè)列表selectors:列表中的每一項(xiàng)是一層導(dǎo)航對應(yīng)的選擇器,比如下標(biāo)為0的元素是第一級導(dǎo)航,通常可以用選擇器"h1",下標(biāo)為1的元素是第二級導(dǎo)航,通常可以用選擇器"h2";

一個(gè)字符串exceptSelector,用于排除例外元素的選擇器;

一個(gè)回調(diào)函數(shù)callback,用于接收生成的導(dǎo)航樹形數(shù)據(jù)。

三、具體實(shí)現(xiàn) 1. 錨點(diǎn)生成函數(shù)

需要在每一個(gè)導(dǎo)航元素臨近位置插入一個(gè)錨點(diǎn),我這里插在導(dǎo)航元素前面,所以這個(gè)函數(shù)接收一個(gè)導(dǎo)航元素dom參數(shù),并生成一個(gè)元素插入到dom之前。代碼如下:

import uuidv4 from "uuid/v4"
let ATTR_NAME = "navigation_anchor"
function createLinkElement (dom) {
  let id = uuidv4()
  let element = document.createElement("a")
  element.setAttribute("id", id)
  element.setAttribute(ATTR_NAME, true)
  dom.parentNode.insertBefore(element, dom)
  return id
}

這個(gè)函數(shù)接收導(dǎo)航元素dom作為參數(shù),生成一個(gè)a標(biāo)簽,并且給a標(biāo)簽設(shè)置了一個(gè)uuid(確保唯一性)作為id,同時(shí)設(shè)置了一個(gè)特殊屬性"navigation_anchor"(盡可能復(fù)雜,你甚至可以用uuid,不要與DOM中其他元素屬性相同)便于清理所有生成的錨點(diǎn)。

2. 錨點(diǎn)清理函數(shù)

用于清除生成的錨點(diǎn)元素。代碼如下:

function clearLinkElement (dom) {
  dom = dom || document
  let domList = dom.querySelectorAll(`a[${ATTR_NAME}]`)
  for (let idx = domList.length - 1; idx > -1; idx--) {
    let element = domList[idx]
    element.parentNode.removeChild(element)
  }
}

可以看到,通過給錨點(diǎn)元素設(shè)置一個(gè)特殊屬性,在清除的時(shí)候非常容易。這里用到一個(gè)非常重要的函數(shù)querySelectorAll,它會根據(jù)調(diào)用的根節(jié)點(diǎn)遍歷該節(jié)點(diǎn)的子DOM樹,返回符合某個(gè)選擇器的NodeList(一個(gè)類數(shù)組的對象,但不是數(shù)組!),而且遍歷方式就是上文所述的深度優(yōu)先先序遍歷!真是激動人心!接下來我們可以用這個(gè)元素獲取所有需要導(dǎo)航的元素列表。

3. 生成樹形導(dǎo)航數(shù)據(jù)函數(shù)

通過傳入的導(dǎo)航元素DOM根節(jié)點(diǎn)、導(dǎo)航元素選擇器列表、導(dǎo)航元素排除選擇器,返回一個(gè)樹形數(shù)據(jù)的列表list。查找出所有導(dǎo)航元素,插入對應(yīng)錨點(diǎn),并將錨點(diǎn)信息和導(dǎo)航元素標(biāo)題存到list中。

function generateNavTree (dom, selectors, exceptSelector) {
  clearLinkElement(dom)
  let list = []
  if (exceptSelector) {
    let exceptList = dom.querySelectorAll(exceptSelector)
    exceptList.forEach(element => {
      element.__nav_except = true
    })
  }
  for (let idx in selectors) {
    let elementList = dom.querySelectorAll(selectors[idx])
    elementList.forEach(element => {
      if (element.__nav_except || element.offsetParent === null) return
      element.__nav_level = idx
    })
  }
  let selector = selectors.join(",")
  let domList = dom.querySelectorAll(selector)
  for (let element of domList) {
    if (!element.__nav_level) {
      delete element.__nav_except
      continue
    }
    let pushList = list
    while (element.__nav_level > 0) {
      pushList = pushList.length ? pushList[pushList.length - 1].children : null
      if (!pushList) break
      element.__nav_level--
    }
    let data = {
      title: element.textContent,
      children: [],
      id: createLinkElement(element)
    }
    pushList && pushList.push(data)
    delete element.__nav_level
  }
  return list
}

到這一步有個(gè)很有必要注意的地方,導(dǎo)航數(shù)據(jù)里的title我最開始用了一個(gè)超級慢的屬性innerText,然后整個(gè)頁面生成導(dǎo)航(大約50個(gè)導(dǎo)航節(jié)點(diǎn))竟然要2s左右,后面改為了才textContent。經(jīng)過我的測試,兩個(gè)屬性的訪問時(shí)間相差n個(gè)數(shù)量級,訪問innerText大約要30ms,而訪問textContent大約要0.05ms左右。就是這么大的差別,查閱了相關(guān)資料,原因應(yīng)該是innerText會引起瀏覽器重排,耗時(shí)超級多。

4. 調(diào)用導(dǎo)航數(shù)據(jù)生成函數(shù)并通過回調(diào)傳給組件。

現(xiàn)在生成導(dǎo)航數(shù)據(jù)的函數(shù)已經(jīng)有了,一個(gè)問題就是何時(shí)調(diào)用此函數(shù)呢?我們通過Vue指令來實(shí)現(xiàn),可以在相應(yīng)的鉤子函數(shù)中調(diào)用。一個(gè)時(shí)機(jī)是當(dāng)指令綁定的元素所在模板更新完成之時(shí),另一個(gè)時(shí)機(jī)是指令綁定元素插入之時(shí)。
指令部分代碼如下:

export default {
  bind (el, binding, vNode) {
    el.__navigationGenerateFunction = () => {
      if (el.__generating) return
      let selectors = binding.value.selectors || ["h1", "h2"]
      let exceptSelector = binding.value.exceptSelector
      el.__generating = true
      let list = []
      generateNavTree(el, selectors, exceptSelector, list)
      binding.value.callback(list)
      vNode.context.$nextTick(() => {
        delete el.__generating
      })
    }
  },
  inserted (el, binding, vNode) {
    el.__navigationGenerateFunction && el.__navigationGenerateFunction()
  },
  componentUpdated (el, binding, vNode) {
    el.__navigationGenerateFunction && el.__navigationGenerateFunction()
  },
  unbind (el, binding, vNode) {
    clearLinkElement()
    if (el.__navigationGenerateFunction) {
      delete el.__navigationGenerateFunction
    }
  }
}

需要注意的是,我們在模板更新完成時(shí)插入錨點(diǎn)元素,而這本身又是會觸發(fā)模板更新的,所以需要打個(gè)標(biāo)記避死循環(huán)。

5. 導(dǎo)航數(shù)據(jù)的展示

導(dǎo)航數(shù)據(jù)是一個(gè)樹形數(shù)據(jù),所以可以用樹形組件來展示之。比如element或者iview的樹組件都可以。不過因?yàn)樵?jīng)對element和iview的樹形組件不甚滿意,自己寫過一個(gè)樹形組simple-vue-tree件并且發(fā)布到了npm。
這里我就使用這個(gè)組件來展示,下面是一個(gè)完整的示例:


四、npm插件

這個(gè)導(dǎo)航工具我已經(jīng)發(fā)布到npm了,地址為vue-outline。如果你需要用到并且不想造輪子的話,可以通過npm或者yarn等包管理工具安裝,并且可以在npm上查看使用方法。

就這樣吧,感謝閱讀。第一次在思否寫文章,之前一直都在CSDN寫博客,不過CSDN太舊了,以后就轉(zhuǎn)到思否吧。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/108766.html

相關(guān)文章

  • Win10應(yīng)用設(shè)計(jì)的那些事兒

    摘要:如何挑選合適的導(dǎo)航結(jié)構(gòu)導(dǎo)航設(shè)計(jì)是應(yīng)用設(shè)計(jì)的關(guān)鍵,設(shè)計(jì)規(guī)范以下簡稱規(guī)范中將導(dǎo)航元素分為對等層次和歷史導(dǎo)航等幾類,例如表和透視表導(dǎo)航窗格是對等導(dǎo)航元素,中心大綱細(xì)節(jié)屬于分層導(dǎo)航元素,返回則屬于歷史導(dǎo)航元素。 此文已由作者楊凱明授權(quán)網(wǎng)易云社區(qū)發(fā)布。 歡迎訪問網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營經(jīng)驗(yàn)。 繼Windows 10系統(tǒng)發(fā)布之后,很多Windows用戶更新了系統(tǒng)。win10系統(tǒng)的發(fā)布,...

    ad6623 評論0 收藏0
  • HTML入門學(xué)習(xí)筆記(一)

    摘要:元素表示主導(dǎo)鏈接的區(qū)域。已經(jīng)習(xí)慣使用或元素對鏈接進(jìn)行結(jié)構(gòu)化的情況下,并沒有取代這種最佳實(shí)踐,只不過在它們外圍包上了一個(gè)。不允許將嵌套在內(nèi)。 第一章 網(wǎng)頁的構(gòu)造塊 一個(gè)網(wǎng)頁主要包括文本內(nèi)容、對其它文件的引用和標(biāo)記。 語義化HTML:有含義的標(biāo)記 HTML包含關(guān)于文檔中內(nèi)容的信息,這些信息稱作標(biāo)記,用以描述內(nèi)容的含義,即語義。也就是說,HTML僅僅關(guān)心網(wǎng)頁中要展示的內(nèi)容,至于如何展示,那是...

    Miracle 評論0 收藏0
  • uni-app 創(chuàng)建的第一個(gè)應(yīng)用

    摘要:體驗(yàn)并不好在中,有這個(gè)例子,參考使用即可做出類似微信通訊錄的頁面。啟動頁計(jì)劃是不顯示導(dǎo)航欄的,為了跳過啟動頁,添加了一個(gè)跳過按鈕。 本人微信公眾號:前端修煉之路,歡迎關(guān)注 背景介紹 經(jīng)過上一篇文章uni-app官方教程學(xué)習(xí)手記的學(xué)習(xí)之后,我就著手做這個(gè)項(xiàng)目了。 目前已經(jīng)初步搭出了整體的框架,秉著取之于社會,回饋于社會的原則,我將這個(gè)項(xiàng)目開源到GitHub uni-shop,發(fā)展壯大un...

    tianlai 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<