通过codemirror实现一个可插入自定义标签的textarea

写在开头

本章来分享一个小案例,具体效果如上图所示。⬆

该案例主要通过 CodeMirror 来实现,而 CodeMirror 是一个通过 JS 实现的Web代码编辑器组件,它带有大量的语言模式、CSS主题和很多高级的插件功能,还允许进一步扩展,总之它非常强大。😋

是一个很不错的产品,值得推荐入手。👻

本次小编使用的是 CodeMirror5 版本,最新版本已经到 6 了,而且开发者们也在积极维护。

这两个版本差别还是挺大的,不过呢,对本篇文案的案例影响却不大,我们只是使用到了其中一个很小的功能,所以你想使用哪个版本都是可以的。

贴两个版本的文档地址,可以自己瞅瞅:😋

codemirror5文档

codemirror6文档

正文

接下来就开始进入主题了。👏

安装:

javascript 复制代码
npm install codemirror@5.65.14

演示部分小编选择使用 Vue,其他技术栈都是可以的。

页面结构:

html 复制代码
<template>
  <div>
    <div class="textarea-editor">
      <textarea ref="textarea" class="textarea" />
    </div>
    <input v-model="content" placeholder="请输入标签内容" />&nbsp;
    <button @click="addTag">添加</button>
  </div>
</template>

JS 逻辑:

javascript 复制代码
<script>
// 编辑器基本样式
import "codemirror/lib/codemirror.css";
// 提示语样式
import "codemirror/addon/display/placeholder";
import CodeMirror from "codemirror";

export default {
  data() {
    return {
      content: "",
      $cm: null, // 添加 $ 开头能避免实例对象进行依赖收集
    };
  },
  mounted() {
   this.init();
  },
  methods: {
    /** @name 初始化 **/
    init() {
      this.$cm = CodeMirror.fromTextArea(this.$refs.textarea, {
        placeholder: "请输入......",
        lineWrapping: true, // 自动换行
      });
    },
    /** @name 标记 **/
    markVariable(mark) {
      // 创建自定义的标签元素
      const spanDom = document.createElement("span");
      const label = mark.variable;
      spanDom.title = label;
      spanDom.innerText = label;
      spanDom.classList.add("textarea-tag");
      spanDom.dataset.variable = mark.variable;
      // 标记特定的文本:https://codemirror.net/5/doc/manual.html#api_history
      this.$cm.markText(mark.start, mark.end, {
        replacedWith: spanDom, // 将特定位置的文本替换成给定的节点元素,必须是行元素,不能是块元素
        atomic: true, // 原子化,会把节点元素当成一个整体,光标不会进入其中
      });
    },
    /** @name 插入 **/
    insertVariable(content) {
      if (!content) return;
      // 获取自定义标签未插入之前的光标对象,记录了光标的位置和状态
      const cursor = this.$cm.getCursor();
      // 将自定义标签插入textarea中:https://codemirror.net/5/doc/manual.html#api_selection
      // 在前后加上 ##与 $$ 只是方便我们后期getValue()的时候好进行内容匹配
      this.$cm.replaceSelection(`##${content}$$`);
      // 将插入的自定义标签进行标记(将 ##${content}$$ 变成 <span>{content}</span>)
      this.markVariable({
        start: cursor,
        end: this.$cm.getCursor(),  // 获取自定义标签插入之后的光标对象
        variable: content,
      });
      this.$cm.setCursor(this.$cm.getCursor());
      this.$cm.focus();
    },
    /** @name 添加自定义标签 **/
    addTag() {
      this.insertVariable(this.content);
    },
    /** @name 获取值 **/
    getValue() {
      const value = this.$cm.getValue();
      console.log(value);
    },
  },
};
</script>

Em......以上代码小编都写上了详细的注释,代码也缩减到不能再缩啦,应该不是很难,相信都能看懂啦。😉

可能需要注意的是 CodeMirror 的光标对象:

CodeMirror 给我们提供了很多操作输入内容的方法,很多时候都需要我们提供这个光标对象。

最后就是样式啦:

javascript 复制代码
<style scoped>
.textarea-editor {
    width: 300px;
    height: 140px;
    border: 1px solid rgb(234, 234, 234);
    font-size: 14px;
    overflow: hidden;
    position: relative;
    cursor: text;
    margin-bottom: 10px;
}
.textarea-editor:hover {
    border-color: #6c8dff;
}
.textarea {
    width: 100%;
    height: 100%;
    border: none;
    background-color: transparent;
}
</style>

<style>
/* 全局样式,覆盖codemirror一些基本样式 */
.textarea-editor .CodeMirror {
    width: 100%;
    height: 100%;
}
.textarea-editor pre.CodeMirror-line,
.textarea-editor pre.CodeMirror-line-like {
    line-height: 24px;
    outline: none;
}
.textarea-tag {
    width: fit-content;
    height: 24px;
    line-height: 24px;
    text-align: center;
    min-width: 60px;
    max-width: 260px;
    font-size: 12px;
    border-radius: 11px;
    color: #fff;
    background-color: #fab30b;
    box-sizing: border-box;
    padding: 2px 6px;
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
}
</style>

以上就是这个案例的全部源码内容啦。👏

结尾

可能有小伙伴会在想做一个可插入自定义标签的 textarea ,需要那么麻烦专门去引入 CodeMirror 吗?

当然不需要,如果仅仅是这个功能的话❗

但是,由于小编业务中对这个 textarea 还有很多其他需求,比如字体颜色的处理、智能提示、复制粘贴、兼容性问题等等,所以选择 CodeMirror 可以帮忙省下很多麻烦事。


至此,本篇文章就写完啦,撒花撒花。

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。

老样子,点赞+评论=你会了,收藏=你精通了。

相关推荐
别拿曾经看以后~11 分钟前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
我要洋人死14 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人26 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人26 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR32 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香34 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969337 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai42 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍