contenteditable实现插入标签的输入框功能(Vue3版)

需求:实现一个简易的函数编辑器

  1. 点击参数能够往输入框插入标签
  2. 点击函数能够往输入框插入文本
  3. 删除能够把标签整体删除
  4. 输入的参数能够获取到其携带的信息
    在这里插入图片描述
    在这里插入图片描述

插入文本

/**
 * @description 点击函数展示到输入框
 */
const getValue = ({ item, type }: any) => {
  // 创建一个文本节点
  const textNode = document.createTextNode(item.value);
  // 在光标位置插入文本节点
  range.value!.insertNode(textNode);
  // 移动光标到文本节点的末尾
  range.value?.setStartAfter(textNode);
  // 折叠光标到文本节点的末尾
  range.value?.collapse(true);
  // 移除所有选区 不移除selection会到聚焦点击的文本
  selection.value?.removeAllRanges();
  // 添加选区
  selection.value?.addRange(range.value!);
};

插入标签

/**
 * @description 点击参数展示到输入框
 */
const getSpanTag = (params: DatabaseManagementParamsForm) => {
  // 创建前缀
  let prefix = `<span contenteditable="false" disabled="disabled" class="el-tag el-tag--primary el-tag--small fn-param" data-param="${params.id}">`;
  // 创建后缀
  let suffix = "</span>";
  // 创建span元素
  let el = document.createElement("span");
  // 将前缀和后缀插入span元素
  el.innerHTML = prefix + params.abbreviation + suffix;
  // 去掉外层的span
  let frag = document.createDocumentFragment();
  let node = frag.appendChild(el.firstChild!);
  // 插入tag
  range.value?.insertNode(node);
  // 设置光标
  range.value?.setStartAfter(node);
  range.value?.collapse(true);
  // 不移除selection会到聚焦点击的文本
  selection.value?.removeAllRanges();
  // 添加选区
  selection.value?.addRange(range.value!);
};

提交获取数据

/**
 * @description 获取构建的html里面的文本和参数
 */
const getTextAndParams = () => {
  // 获取文本中的参数元素
  const paramsEls = textRef.value?.getElementsByClassName("fn-param");
  // 获取文本的innerHTML
  let innerHTML = textRef.value!.innerHTML;
  // 定义参数数组
  const params = [];
  // 如果参数元素存在
  if (paramsEls) {
    // 遍历参数元素
    for (let index = 0; index < paramsEls.length; index++) {
      // 将参数元素转换为HTMLSpanElement类型
      const element = paramsEls[index] as HTMLSpanElement;
      // 获取参数元素在innerHTML中的索引
      const idx = innerHTML.indexOf(element.outerHTML);
      // 将参数元素添加到参数数组中
      params.push({
        name: element.innerText,
        id: element.dataset.param,
        index: idx,
      });
      // 将参数元素的outerHTML替换为innerHTML
      innerHTML = innerHTML.replace(element.outerHTML, element.innerText);
    }
  }
  // 返回文本和参数数组
  return {
    text: textRef.value!.innerText,
    params,
  };
};

在这里插入图片描述

index标记tag位置,避免重复无法渲染识别

数据回显

/**
 * @description 根据数据回显
 */
const reviewFn = (data: FnEditorData) => {
  // 获取data中的fn和params
  const { fn, params } = data;
  // 定义innerHTML,初始值为fn
  let innerHTML = fn;
  // 定义idx,初始值为0
  let idx = 0;
  // 遍历params,将每个param的name插入到innerHTML中
  params.forEach((item) => {
    // 定义prefix,用于创建span标签
    let prefix = `<span contenteditable="false" disabled="disabled" class="el-tag el-tag--primary el-tag--small fn-param" data-param="${item.id}">`;
    // 定义suffix,用于结束span标签
    let suffix = "</span>";
    // 定义tag,用于拼接prefix和suffix
    const tag = prefix + item.name + suffix;
    // 使用正则替换innerHTML中的item.name,替换为tag
    innerHTML = innerHTML.replaceAll(item.name, (match, offset) => {
      // 如果offset等于item.index加上idx,则替换为tag
      if (offset === item.index + idx) {
        return tag;
      }
      // 否则,返回match
      return match;
    });
    // 将tag的长度减去item.name的长度,加到idx上
    idx += tag.length - item.name.length;
  });
  // 延迟执行,将innerHTML设置到textRef的value上
  nextTick(() => {
    textRef.value!.innerHTML = innerHTML;
  });
};

目前只是简单的demo,如果涉及到公式渲染就是另外的价钱了,数学公式渲染参考katex

在线浏览
代码仓库

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值