V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
shubei
V2EX  ›  程序员

请教一下,如何用 js 合并连续的相同的 span

  •  
  •   shubei · Nov 2, 2021 · 3103 views
    This topic created in 1640 days ago, the information mentioned may be changed or developed.

    =>

    情已 逐晓云空

    29 replies    2021-11-02 19:11:07 +08:00
    shubei
        1
    shubei  
    OP
       Nov 2, 2021
    <\span>高<\/span>
    <\span class="del">情<\/span>
    <\span class="del">已<\/span>
    <\span class="match"><\span class="add">逐<\/span><\span class="add">晓<\/span><\/span>
    <\span>云</span>
    <\span>空</span>
    shubei
        2
    shubei  
    OP
       Nov 2, 2021
    .. 可以注入的竟然
    shubei
        3
    shubei  
    OP
       Nov 2, 2021
    <\span>高<\/span>
    <\span class="del">情<\/span>
    <\span class="del">已<\/span>
    <\span class="match"><\span class="add">逐<\/span><\span class="add">晓<\/span><\/span>
    <\span class="match"><\span>云</span><\span>空</span><\/span>

    =>

    <\span>高<\/span>
    <\span class="del">情已<\/span>
    <\span class="match"><\span class="add">逐晓<\/span><\span>云空</span><\/span>
    AoEiuV020
        4
    AoEiuV020  
       Nov 2, 2021
    感觉只能普通的循环遍历然后一个一个判断构造新的,
    kkocdko
        5
    kkocdko  
       Nov 2, 2021 via Android
    @shubei 不是注入,是 markdown
    zhea55
        6
    zhea55  
       Nov 2, 2021
    用 jquery 的话,非常简单。

    找到这些所有 span 的父节点

    $(parentNode).text()
    waiaan
        7
    waiaan  
       Nov 2, 2021
    @zhea55
    它要合并相同 class 的文字,这个就只能遍历了。
    zhea55
        8
    zhea55  
       Nov 2, 2021   ❤️ 1
    @waiaan 哦,感觉有发帖这个时间,代码都写完了。


    浪费时间、浪费生命。
    shubei
        9
    shubei  
    OP
       Nov 2, 2021
    主要有 match 这种的嵌套,双指针好像不好使
    zhea55
        10
    zhea55  
       Nov 2, 2021
    @shubei 迭代的时候,把当前循环元素的 classname 拿出来,

    在当前循环元素的 siblings 里面查找这个选择器。就拿到了所有同类,然后.text(),然后去空格。



    然后下一个的 classname 要是和上次循环的一样,continue


    再说下去,代码都给你写好了。
    rongchuan
        11
    rongchuan  
       Nov 2, 2021
    最简单的方式,获取 dom 字符串,用正则匹配处理,想咋弄都行。弄完如果没有光标需求,直接 innerHTML 完事。要操作光标就用 appendChild
    shubei
        12
    shubei  
    OP
       Nov 2, 2021
    @zhea55 额,首先我这个就是单纯的字符串,并没有渲染,所以 siblings 、text 这些都不能用(因为就是 dom 节点过多,所以在渲染前用 js 合并一下的),然后对于字串符而言,我遇到的第一个问题就是用什么 split ,我最早的版本是用</span>,加了 match 之后不好使了
    zhea55
        13
    zhea55  
       Nov 2, 2021
    @shubei 你这些数据,应该是入库之前就应该把数据整理好。

    入库前爬取程序,一定是有 dom 节点的。


    你要处理字符串的话,即使你用正则表达式,还是容易出现很多异常情况。


    远远没有处理 dom 节点的代码可靠。
    zjsxwc
        14
    zjsxwc  
       Nov 2, 2021
    搞个 html parser https://github.com/andrejewski/himalaya
    后当普通数据处理呗
    shubei
        15
    shubei  
    OP
       Nov 2, 2021
    @zhea55 唉,这就孩子没娘,说来话长了呀。反正现在就堆到我这里了 [大哭]
    cyrbuzz
        16
    cyrbuzz  
       Nov 2, 2021
    这个用 CSS 选择器啦。
    ```
    let plus = document.querySelectorAll("span + span")
    let temp1 = plus[0]
    temp1.previousSibling.innerText += temp1.innerText
    temp1.parentElement.removeChild(temp1)
    ```

    首先遍历一遍 span ,没有 class 的一律先加一个虚拟 class ,然后把所有 class 找出来,去重,所有 class 挨个跑上面的代码,最后把虚拟的 class 去掉就完事了。
    shubei
        17
    shubei  
    OP
       Nov 2, 2021
    @cyrbuzz 兄弟 看 12 楼,我想在渲染之前通过 js 直接合并好
    cyrbuzz
        18
    cyrbuzz  
       Nov 2, 2021
    @shubei

    那你这不 parse 一下处理 DOM ,DOM 没有那么脆弱。刚打开一个 13WDOM 的页面只占用 31MB(当然每个的内容比较少)。
    shubei
        19
    shubei  
    OP
       Nov 2, 2021
    @cyrbuzz 也可能是我别的地方有问题?我是用的 v-html 。原文 span 比较多,接口 200ms ,js 循环一遍 140ms ,但是渲染出来就要 2s 左右了
    ookkxw
        20
    ookkxw  
       Nov 2, 2021
    感觉纯 string 处理难点在于切割同时还要区分有没有子节点,我写的话先全部<\/span>切割掉变成数组,然后循环变成 VDOM tree ,接下来就简单了
    cyrbuzz
        21
    cyrbuzz  
       Nov 2, 2021
    @shubei

    2s 是可接受范围吧,正常浏览网站,像是 bilibili, DOMContentLoad 也要 1s 左右。
    siweipancc
        22
    siweipancc  
       Nov 2, 2021 via iPhone
    两个栈与一个计数器。源数据全部入栈 1 ; 1 出栈做中间计数然后入 2 栈; 1 出栈与计数匹配则 2 出栈合并后入 2 栈不匹配则跳过,同时清空计数。
    while 打上去。
    结尾 2 栈整个导出来渲染。
    效率不会很高。
    iPhone12
        23
    iPhone12  
       Nov 2, 2021
    可以只创建 dom ,不渲染。
    再通过 dom 的 api 进行 siblings 、text 操作拿到 text 。
    iPhone12
        24
    iPhone12  
       Nov 2, 2021
    let temp = document.createElement('div')
    temp.innerHTML = '题上 html 内容'

    // example

    let text = temp.getElementsByClassName('match')[0].innerText
    aguesuka
        25
    aguesuka  
       Nov 2, 2021
    首先使用 DOMParser 解析成 dom 树, 先序遍历元素, 如果前一个元素和后一个元素的 tag 和 attr 相同, 则将后一个元素的子元素都 append 到前一个元素中, 并删除后一个元素.
    2i2Re2PLMaDnghL
        26
    2i2Re2PLMaDnghL  
       Nov 2, 2021
    同上,new DOMParser().parseFromString(str, 'text/html') ,用 DOM 操作改完之后再转回 html 插入
    然后从根节点开始对 children 作两次循环,第一遍吸收 nextSibling ,第二遍递归下降调用自身
    autoxbc
        27
    autoxbc  
       Nov 2, 2021
    强行在 String 阶段处理,本质就是写了一个简陋的 DOM 解析器
    aguesuka
        28
    aguesuka  
       Nov 2, 2021
    简单地写了一下, 还可以继续优化, 不过这样应该够用了.

    function isText(target, node) {
    return target !== null && target.nodeType === Node.TEXT_NODE
    && node.nodeType === Node.TEXT_NODE
    }

    function shouldMerge(target, node) {
    if (target === null) {
    return false
    }

    return target.nodeType === Node.ELEMENT_NODE
    && node.nodeType === Node.ELEMENT_NODE
    && node.getAttribute('class') === target.getAttribute('class')
    }

    /**
    * @param rootNode {Node}
    */
    function mergeChildrenNodes(rootNode) {
    if (!rootNode.hasChildNodes()) {
    return;
    }
    let preChild = rootNode.firstChild;
    const markToRemove = []
    for (let i = 1; i < rootNode.childNodes.length; i++) {
    const child = rootNode.childNodes[i];
    if (isText(preChild, child)) {
    preChild.textContent += child.textContent;
    markToRemove.push(child);
    } else if (shouldMerge(preChild, child)) {
    child.childNodes.forEach(node => preChild.appendChild(node));
    markToRemove.push(child);
    } else {
    preChild = child;
    }
    }
    markToRemove.forEach(node => node.parentNode.removeChild(node));
    for (const node of rootNode.childNodes) {
    mergeChildrenNodes(node);
    }
    }

    function test() {
    const dom = new DOMParser().parseFromString(`<\span>高<\/span>
    <\span class="del">情<\/span>
    <\span class="del">已<\/span>
    <\span class="match"><\span class="add">逐<\/span><\span class="add">晓<\/span><\/span>
    <\span class="match"><\span>云</span><\span>空</span><\/span>`, 'text/html')
    mergeChildrenNodes(dom)
    const theSpanWitchClassIsDel = dom.lastChild.lastChild.childNodes[1];
    console.info(theSpanWitchClassIsDel)
    console.assert(theSpanWitchClassIsDel.getAttribute)
    console.assert(theSpanWitchClassIsDel.textContent === '情已')
    }
    shubei
        29
    shubei  
    OP
       Nov 2, 2021
    @aguesuka 学习了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   810 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 86ms · UTC 20:22 · PVG 04:22 · LAX 13:22 · JFK 16:22
    ♥ Do have faith in what you're doing.