首页 > 试题广场 >

用js实现一个用户行收集器,功能如下: 1,收集

[问答题]
用js实现一个用户行收集器,功能如下:

1,收集用户在页面中的所有click行为,并使用log方法发送日志,包含触发事件的节点xpath信息

2xpath需包含tagNameidclass、同级同名节点索引(1开始),如

<body>

<div id=“container>

<p>something</p>
<a id=“link1 class=link-class></a>
<a id=
link2 class=link-class current>target link</a>

</div> 

</body>

点击target link时,xpath body[1]/div[1][@id=“container”]/a[2][@id=“link2”][contains(@class, “link-class")][contains(@class, “current")]

3,不侵入、不影响其他业务代码的执行


改了下前面兄弟的代码,因为上一位兄弟的代码中无法判断隔了一个标签的相同标签
即<a>-><p>-><a>,第二个a应该为a2而不是a1
 // 遍历深度树形结构
        var obody = document.getElementsByTagName('body')[0];
        obody.addEventListener('click', function (e) {
            console.log(getXPath(e.target));
        })

        function getXPath(target) {
            if (target.nodeName == 'BODY') return 'body[1]';
            var index = 1;
            
            var str = '';
            var tempTarget = target;
            var nodeName = target.nodeName;
            var id = target.id;
            var classList = target.classList;
            // 这是在前面一个才可以,如果前面不是一样的标签,再前面有就不可以了
            // while (tempTarget.previousElementSilbling != null && tempTarget.previousElementSilbling.nodeName == nodeName) {
            //     index++;
            //     // 当前一个节点存在且和自己是同一个标签,index++
            //     // 然后把target转到前面的标签(即计算有多少个相同的标签)
            //     tempTarget = tempTarget.previousElementSilbling;
            // }
           
            // 当前面有兄弟节点时,就可以往前
            while(tempTarget.previousElementSibling!=null)
            {
                 //如果前一个节点的node和要找的nodeName一样则index++,把target赋予前一个点
                if(tempTarget.previousElementSibling.nodeName == nodeName)
                {
                    index++;
                    console.log(index);
                } 
                // 无论前面的标签是否和自身一样,但是一直往前,比较是否和自身的nodeName一样
                tempTarget = tempTarget.previousElementSibling;
            }
            
            // 然后就str就是所在的位置
            str = '/' + nodeName.toLowerCase() + '[' + index + '][@id="' + id + '"]';
            classList.forEach(function (ele, index, self) {
                str += '[contains(@class,"' + ele + '")]';
            })
            return getXPath(target.parentNode) + str;
        }
 

发表于 2020-03-12 16:37:59 回复(0)
挺清晰的解法
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style type="text/css">
    </style>
  </head>
  <body>
    <div id="“container”">
      <p id="ypu" class="link-class current">something</p>
      <a id="“link1”" class="“link-class”"></a>
      <a id="“link2”" class="“link-class" current”>target link</a>
    </div>
    <script>
      window.onload = function () {
        let body = document.body;
        body.addEventListener("click", function (event) {
          const rz = fetchDetail(event.target);
          console.lo***击了", rz);
        });
        function fetchAttribute(element) {
          let rz = "";
          let id = element.id,
            classes = element.className;
          id ? (rz += `[@id="${id}"]`) : null;
          if (classes) {
            classes = classes.split(" ");
            classes = classes.map((classes) => {
              return `[contains(@class,"${classes}")]`;
            });
            rz += classes.join(",");
          }
          return rz;
        }
        function fetchDetail(element) {
          let rz = "";
          let parent = element.parentElement;
          if (parent && parent.tagName.toLowerCase() !== "html") {
            if (parent.childNodes) {
              let elementIndex;
              let childArr = [...parent.childNodes].filter((node) => {
                return node.tagName;
              });
              childArr.map((node, index) => {
                if (node === element) {
                  elementIndex = index + 1;
                }
              });
              rz += `${element.tagName.toLowerCase()}[${elementIndex}]`;
              rz += fetchAttribute(element);
              return parent.parentElement
                ? fetchDetail(parent.parentElement) + rz
                : rz;
            }
          } else {
            return `${element.tagName.toLowerCase()}[1]${fetchAttribute(
              element
            )}`;
          }
        }
      };
    </script>
  </body>
</html>


发表于 2021-04-07 09:38:52 回复(0)
 ; (function (document) {
        let body = document.getElementsByTagName("body")[0];
        body.addEventListener('click', (e=> {
            let xpath;
            const getPathX = (el=> {
                let parent = el.parentNode;
                if (el.nodeName == "HTML")
                    return "";
                let els = parent.getElementsByTagName(el.nodeName);
                var index = 0;
                while (els[index] !== el)
                    index++;
                index++;
                let id = el.getAttribute("id");
                let classes = el.className.split(" ");
                let res = `${el.nodeName.toLowerCase()}[${index}]`;
                id && (res += `[@id=${id}]`);
                for (let i = 0i < classes.lengthi++) {
                    (classes[i] != "") && (res += `[contains(@class,${classes[i]})]`);
                }
                if (el.nodeName == "BODY")
                    return getPathX(parent) + res;
                else
                    return getPathX(parent) + "/" + res;

            }
            console.log(getPathX(e.target));
        }, false);

    })(document)
发表于 2021-03-21 12:17:08 回复(0)
这个应该就可以解决很多情况了,每个功能分成一个模块来写,应该挺清晰易懂的
(function(){
            var body = document.body;
            body.addEventListener('click', function(e){
                var xpath = ''
                var parent = e.target.parentNode
                var ele = e.target
                getBase(ele, parent) // 给每个节点添加一个index属性
                var index = e.target.index
                var parentArr = getParent(ele, [])
                // 取出最后两个dom节点,因为是html和document,这里并用不到
                // 翻转过来,让父级在前
                parentArr = parentArr.slice(0, parentArr.length - 2).reverse()

                for(let i = 0; i < parentArr.length; i++){
                    let index = `[${parentArr[i].index}]`
                    let classStr = getClassStr(parentArr[i])
                    let idStr = getIdStr(parentArr[i])
                    xpath = xpath + parentArr[i].tagName.toLocaleLowerCase() + index + idStr + classStr
                }

                // 这里也可以单独封装一个函数,我就懒得弄了
                let domClassStr = getClassStr(ele)
                let domIdStr = getIdStr(ele)
                let domIndex = `[${ele.index}]`
                let domName = ele.tagName.toLocaleLowerCase() + domIndex
                xpath = xpath + domName + domIdStr + domClassStr
                console.log(xpath)
            }, false)

            // 获取id组合的字符串
            function getIdStr(ele){
                let id = ele.id
                if(!id) return ''
                return `[@id="${id}"]`
            }

            // 获取class组合的字符串
            function getClassStr(ele){
                let classList = Array.from(ele.classList)
                if(!classList) return ''
                let str = ''
                for(let i = 0; i < classList.length; i++){
                    let val = `[contains(@class, "${classList[i]}")]`
                    str += val
                }
                return str
            }

            // 获取当前点击dom的所有父级
            function getParent(ele, arr){
                var father = ele.parentNode
                getBase(ele, father)
                arr.push(father)
                if(father.parentNode){
                    getParent(father, arr)
                }
                return arr
            }

            // 给每一个同级dom都添加一个index属性
            function getBase(tag, parent){
                let children = Array.from(parent.children)
                for(let i = 0; i < children.length; i++){
                    if(children[i].tagName != tag.tagName){
                        children.splice(i, 1)
                        i--
                    }else{
                        children[i].index = i + 1
                    }
                }
            }
        })()


发表于 2020-09-13 16:44:49 回复(0)
楼上的兄第们答案可以借鉴

发表于 2020-08-25 00:39:28 回复(0)
document.body.onclick = function(e) {
      let res = [];  //用于存放每一级节点拼接成的字符串
      function getXPath(curr) {
        if (curr === document.body) {
          res.unshift('body[1]');
          return;
        };
        let index = 1, part = '';  //part用于保存当前节点拼接出的字符串
        let bros = curr.parentNode.children;
        for (let k = 0; k < bros.length; k++) {  //找出当前节点在同级同名节点中的索引
          if (bros[k] === curr) break;
          if (bros[k].tagName == curr.tagName) index++;
        };

        //开始拼接字符串,标签名、索引、id、class
        part += `${curr.tagName.toLowerCase()}[${index}]`;
        if (curr.id !== '') {
          part += `[@id="${curr.id}"]`;
        };
        if (curr.classList.length != 0) {
          for (let m = 0; m < curr.classList.length; m++) {
            part += `[contains(@class, "${curr.classList[m]}")]`;
          }
        };
        res.unshift(part);  //将拼接好的字符串插入res数组的头部
        getXPath(curr.parentNode);  //向节点的父级递归
      };
      getXPath(e.target);
      console.log(res.join('/'));  //将各个字符串以"/"连接并输出
    };
编辑于 2020-08-11 15:30:21 回复(0)
思路:遍历深度树形结构
var obody = document.getElementsByTagName('body')[0];
obody.addEventListener('click', function(e) {
    console.log(getXPath(e.target));

}, false);

// 获取它的   上级节点至根节点body
// 每一个节点都得获取:
// 获取它的ID
// 获取它的class
// 获取它在同级同名节点中排老几

function getXPath(target) {
    if(target.localName == 'body') return 'body[1]';
    var index = 1;
    var tempTarget = target;
    var str = '';
    var localName = target.localName;
    var id = target.id;
    var classList = target.classList;
    while(tempTarget.previousElementSibling != null && tempTarget.previousElementSibling.localName == localName) {
        index ++;
        tempTarget = tempTarget.previousElementSibling;
    }
    str = '/' + localName + '[' + index + '][@id="' + id + '"]';
    classList.forEach(function(ele, index, self) {
        str += '[contains(@class,"' + ele + '")]';
    })
    return getXPath(target.parentNode) + str;
}

发表于 2020-03-10 21:15:36 回复(0)
(function(w) {
    const getInfo = (dom) => {
        let info = '';
        if (dom.nodeType === 1) {
            const {
                id
            } = dom;
            const classList = [].slice.call(dom.classList);
            const tagName = dom.tagName.toLowerCase();
            let index = 1;
            if (dom === document.body) {
                index = 1;
            } else if (dom.parentNode) {
                const siblings = dom.parentNode.getElementsByTagName(tagName);
                index = [].slice.call(siblings).indexOf(dom) + 1;
            }
            info += `${tagName}[${index}]`;
            if (id) {
                info += `[@id="${id}"]`;
            }
            if (classList) {
                info += classList.map(item => `[contains(@class,"${item}")]`).join('');
            }
        }
        return info;
    }
    const logHandler = (evt) => {
        //
        let log = [];
        let target = evt.target;
        while(target && target !== document.documentElement) {
            log.unshift(getInfo(target));
            target = target.parentNode;
        }
        console.log(log.join(''));
    }
    w.addEventListener('click', logHandler);
})(window);
发表于 2020-02-25 21:06:22 回复(0)