可視化埋點(diǎn)元素圈選器實(shí)現(xiàn)源碼
引言
在實(shí)現(xiàn)可視化埋點(diǎn)的過程中,元素圈選是其功能中不可或缺的一環(huán),其能力具備一定的通用性,故將其邏輯從 可視化埋點(diǎn)平臺 中剝離出來,單獨(dú)作為一個獨(dú)立的工具方法暴露出來,源代碼及演示可直接在 github倉庫中 查看。本文主要是對其實(shí)現(xiàn)的拆解和其中關(guān)鍵點(diǎn)的記錄。
整體流程
整體的 demo 可以在此處查看。
整體來講,圈選器的功能在于:
enable 前,用戶可自由交互,此時(shí)點(diǎn)擊、移動并不會被阻礙。
而 enable 后,當(dāng)用戶移動鼠標(biāo)/移動端移動手指,便會高亮當(dāng)前選擇的元素的大小、padding 及 margin 等。
而當(dāng)用戶鼠標(biāo)點(diǎn)擊 / 移動端手指離開 后,將會觸發(fā)選中的回調(diào),可視化埋點(diǎn)平臺在這一環(huán)節(jié)喚起 埋點(diǎn)錄入表單。
從整體的思路上來講,整個 元素圈選器 的核心功能在于:
- 計(jì)算元素的大小、位置,屬性,并增加蒙層
- 列表元素的判定、多選
- 兼容 pc/mobile
除此之外需要具備 開關(guān)能力,以免影響用戶正常交互。
元素位置、大小及屬性計(jì)算
首先是元素的位置計(jì)算,一個非常簡單的方法是借助現(xiàn)成的 api: Element.getBoundingClientRect 。
通過該方法拿到的 left, top,便是元素相對于視區(qū)左上角的位置,這樣在后續(xù)添加蒙層的時(shí)候,以此作為元素位置即可。
但是實(shí)際在添加蒙層時(shí),左上角是包含了 margin 的位置,故此處通過 left - 'margin-left', top - 'margin-top' 作為元素在左上角的位置。
而對于元素的大小,可以通過 element.offsetWidth 來獲取,這一值包含了元素的 border padding 及 content,故元素實(shí)際的寬高需要減去左右 border 和 左右 padding,才是元素的 實(shí)際寬高。
對于元素的屬性:margin, padding,border,可以通過 Window.getComputedStyle 獲取。
得到上述數(shù)據(jù)后,整個元素的位置、大小、margin/padding/border 值都得到了完整的值,此時(shí)便可以按照這一尺寸繪制元素的蒙層。
但是在實(shí)際的場景中,還存在元素通過 transform 后縮放的場景,此處對上述用到的 api 和 transform scale 的關(guān)系進(jìn)行梳理。
- 獲取元素的位置,Element.getBoundingClientRect,獲取到的是縮放后的位置。
- 獲取元素的大小,offsetWidth,獲取到的是縮放前的寬高
- 獲取元素的屬性,padding/margin/border,Window.getComputedStyle,獲取到的是縮放前的值。
可以看出來,元素的位置是縮放后的,而大小、屬性是縮放前的,實(shí)際蒙層的位置和大小是無法對應(yīng)的。
此時(shí)有兩種方案,一種是根據(jù)縮放比例,計(jì)算縮放后的大小、屬性,另一種方式是直接在父元素上追加同等的縮放比例,從而獲取到實(shí)際的蒙層大小。本文采用的是后一種方案。
通過 ele.offsetWidth / getComputedStyle().width 拿到元素本身的縮放比例,此時(shí)對蒙層父元素追加反向比例的縮放,即可正確添加縮放后的蒙層。此時(shí),由于位置一直取的是左上角,故實(shí)際并不需要關(guān)心元素的 transform-origin,始終使用左上角即可保證蒙層的位置正確。
但是實(shí)際處理時(shí),由于元素的位置是 left - 'margin-left' 獲得的,此時(shí)由于 left 是縮放后的,而 margin-left 是縮放前,所以此處還需要對 left 乘上比例后再相減,實(shí)際的 left 值計(jì)算出后,再除以縮放比例即可解決。
實(shí)際的場景中,還存在一種情況,就是對整個 html 文檔流的縮放,這一場景是在一些 h5 頁面,需要兼容 pc 12px 的字體時(shí),以前一些舊的頁面會先對 整個頁面按照放大 3 倍的尺寸開發(fā),然后在最外層再套一個 transform: scale(0.333) 來實(shí)現(xiàn)對 12px 字體的兼容。
要兼容該場景,首先需要全局插入一個輔助元素,用于檢測 html 上的縮放。
元素的大小、位置及屬性計(jì)算不進(jìn)行修改,全部使用縮放前的值,但是蒙層父元素的縮放比例需要進(jìn)行調(diào)整,從原本的 僅進(jìn)行元素的縮放,改為進(jìn)行元素的縮放并還原 html 的縮放。
這是因?yàn)橛捎诿蓪颖旧肀贿M(jìn)行了縮放,而元素也被進(jìn)行了自身和 html 的雙重縮放,所以蒙層父元素僅需要按照元素的實(shí)際縮放比例進(jìn)行縮放,但是實(shí)際由于蒙層還被 html 的縮放了一層,故需要針對性的抵消縮放比例才可以正常展示蒙層的大小。
以上便是整個元素大小、位置及屬性獲取的方案,也解決了邊界的 transform 場景,實(shí)際中還會有一些額外的處理,比如 元素的 tips 由于存在文字,其展示就不進(jìn)行元素大小的縮放,僅抵消掉 html 帶來的縮放比例即可。
列表元素的判定
在實(shí)際的頁面中,往往存在列表元素,這些元素結(jié)構(gòu)類似但是每一行又有數(shù)據(jù) or 樣式上的差別,對于這種元素,在可視化埋點(diǎn)中,往往需要智能檢測且需要批量選擇。
對于列表場景,每一行往往有跡可循,而判定列表元素,往往也是找到一行。當(dāng)然,實(shí)際的判定還是需要按照一定的規(guī)則,在這里,我定的幾個判定規(guī)則有:
- 當(dāng)前元素的子節(jié)點(diǎn),最少具有 5個,且相同 tag 及相同 className 的數(shù)量要大于 70%。
- 當(dāng)前元素的孫節(jié)點(diǎn)們的 tag 連接起的字符串,相同的數(shù)量要超過 70%。
- 如果不滿足,則從其父節(jié)點(diǎn)再開始查找,一直到 document.body 為止。
通過這樣的方式,實(shí)測能夠覆蓋業(yè)務(wù)中的大部分列表場景。
掛載位置
在實(shí)際可視化埋點(diǎn)過程中,圈選蒙層的掛載位置,一開始是放在 body 的最后一個元素,但是實(shí)際場景中,會存在動態(tài) modal 這樣的場景,會動態(tài)的在 body 最后追加元素,此時(shí)該元素的 xpath 便會收到蒙層元素的影響,導(dǎo)致統(tǒng)計(jì)偏差,故在參考 vconsole 后,將元素轉(zhuǎn)移到了 html 下,從而減少對業(yè)務(wù)元素的影響。
pc/mobile 兼容
pc 端 與 mobile 端,一方面是將 mousemove 替換為 touchmove。
而另一方面,圈選結(jié)束的時(shí)機(jī)在 mobile 端丟失。原本 pc 端通過 body 上捕獲階段的點(diǎn)擊事件進(jìn)行圈選結(jié)束的判定,而 mobile 端,由于 click 事件在 touchmove 后不會觸發(fā),故需要在 touchend 中創(chuàng)建自定義事件,觸發(fā) body 上的點(diǎn)擊。
除此之外,mobile 端還存在移動觸發(fā)頁面滾動的情況,此處本文采用了對所有元素增加 touch-action: none 的方法,避免了移動端手指滾動時(shí)對全局頁面的影響。
同時(shí),移動端 touchmove 的 target 并不會指向當(dāng)前 move 的元素,需要使用一些 api 進(jìn)行當(dāng)前元素的獲取,偽代碼如下:
if (e instanceof TouchEvent && e.touches) { const changedTouch = e.changedTouches[0]; return document.elementFromPoint(changedTouch.clientX, changedTouch.clientY); }
同時(shí),由于該方案回獲取當(dāng)前位置的元素,也就是說如果手指下方的元素是蒙層元素,那么也會被選中,所以需要對 蒙層等無關(guān)元素增加 touch-action: none 來避免被選中。
至此,便完成了 mobile 端的兼容。
總結(jié)
整體來講,圈選器的實(shí)現(xiàn)并不復(fù)雜,麻煩的點(diǎn)主要集中在特殊場景的處理及 dom 的操作上。
元素位置、大小、屬性的獲取,是否受 transform scale 影響,是否存在 html 上的縮放,都是一些常見的邊界條件。
而移動端的兼容、列表元素的判定,也為可視化埋點(diǎn)的整體能力進(jìn)行了增強(qiáng)。
同時(shí),此 dom-inspector-pro,也在 api 上進(jìn)行了拓展,更多的回調(diào)也能滿足更多的場景使用。
以上就是可視化埋點(diǎn)元素圈選器實(shí)現(xiàn)源碼的詳細(xì)內(nèi)容,更多關(guān)于可視化埋點(diǎn)元素圈選器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Svelte調(diào)試模式j(luò)s級別差異和細(xì)化后的體積差異詳解
這篇文章主要為大家介紹了Svelte調(diào)試模式j(luò)s級別差異和細(xì)化后的體積差異詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12微信小程序 require機(jī)制詳解及實(shí)例代碼
這篇文章主要介紹了微信小程序 require機(jī)制詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-12-12Nest.js 之依賴注入原理及實(shí)現(xiàn)過程詳解
這篇文章主要為大家介紹了Nest.js 之依賴注入原理及實(shí)現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01微信小程序 實(shí)戰(zhàn)實(shí)例開發(fā)流程詳細(xì)介紹
這篇文章主要介紹了微信小程序 實(shí)戰(zhàn)實(shí)例開發(fā)流程詳細(xì)介紹的相關(guān)資料,這里主要介紹微信小程序的開發(fā)流程和簡單實(shí)例,需要的朋友可以參考下2017-01-01微信小程序 出現(xiàn)錯誤:{"baseresponse":{"errcode":-80002,"errmsg":""}}解決
這篇文章主要介紹了微信小程序 出現(xiàn)錯誤:{"baseresponse":{"errcode":-80002,"errmsg":""}}解決辦法的相關(guān)資料,需要的朋友可以參考下2017-02-02