解决v-for渲染vue-diff插件后无法跨段复制单侧文本内容

在之前的博客中有通过vue-diff实现两侧文本对比的功能,由于博主的使用场景是需要在一个页面中用v-for来渲染多个vue-diff插件实现文本对比的,这个时候发现在多段的基础上想跨段落复制单侧的文字内容会把另外一侧的内容也给选中,基于这个问题做出了一下解决方案:

vue-diff插件使用请参考:vue3使用vue-diff插件实现文本对比_vue-diff文本比较插件-CSDN博客

解决思路:

通过user-select控制左/右两侧文本的选择行为,在用户交互时动态禁用或启用文本选择功能

  1. 事件时序控制

    1. selectstart:选择操作开始前锁定区域

    2. mousedown:物理点击时立即重置状态

  2. CSS控制策略

    user-select: none;  /* 禁用选择 */
    user-select: text;  /* 恢复选择 */

    相比disabled属性方案的优势:

    • 保持元素可交互状态

    • 避免视觉样式冲突

    • 支持平滑过渡动画

  3. DOM遍历优化

    • 使用classList.contains替代className匹配

    • 通过父容器(.vue-diff-row)缩小查询范围

    • 批量操作替代逐元素处理

代码示例:

<div v-for="(item, index) in compareData" :key="index">
        <Diff
          mode="split"
          theme="light"
          language="text"
          :prev="item.originalText"
          :current="item.translationText"
        />
</div>
nextTick(() => {
  // 监听 selectstart 事件(用户开始选择文本时触发)
  document.addEventListener("selectstart", (e) => {
    if (e.target) {
      // 判断目标元素是否在右侧类名区域内
      let currentIndex = handleClosest(
        e.target,
        "vue-diff-cell-added",
        "vue-diff-row"
      )
        ? 0 // 如果用户选中的是右侧元素,currentIndex 设为 0,将左侧禁用
        : 1; // 否则设为 1,将右侧禁用

      // 遍历所有 .vue-diff-row 元素
      document.querySelectorAll(".vue-diff-row").forEach((row) => {
        let codeList = row.querySelectorAll(".code");
        if (codeList) {
          // 禁用对应索引的 .code 元素的文本选择
          codeList[currentIndex].style.userSelect = "none";
        }
      });
    }
  });

  // 监听 mousedown 事件(鼠标按下时触发)
  document.addEventListener("mousedown", () => {
    // 启用所有 .code 元素的文本选择
    document.querySelectorAll(".code")?.forEach((val) => {
      val.style.userSelect = "text";
    });
  });
});

// 判断目标元素target是否在右侧目标元素内,一直溯源的sourceName根元素
const handleClosest = (target, parentName, sourceName) => {
  if (target.classList?.contains(sourceName)) {
    return false; // 目标元素是 sourceName 类,返回 false
  } else if (target.classList?.contains(parentName)) {
    return true; // 目标元素或父元素是 parentName 类,返回 true
  } else {
    // 递归向上查找父元素
    return handleClosest(target.parentNode, parentName, sourceName);
  }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值