【Vue】div标签实现输入框,利用contenteditable=“true“属性的标签实现

news2024/11/17 17:40:25

推荐个链接🔗,可以更好的查阅自己遇到的问题(点击此处即可跳转)

使用 div 实现 input、textarea 输入框

<template>
  <div class="content">
    <div class="main editTextList" >
      <div
        class="main-content dzm-textarea editText"
        contenteditable="true"
        placeholder="请输入内容"
        id="content"
        @compositionstart="navCompositionstart"
        @compositionend="navCompositionend"
        @keyup="navKeyup"
        @input="navInput"
        @focus="navFocus"
        @propertychange="navPropertychange"
        @paste="optimizePasteEvent"
        ref="comment"
      ></div>
      <div class="main-num" id="num"><span id="total">0</span>/100</div>
    </div>
    <div class="three">
      <!-- 选择表情 -->
      <el-popover trigger="hover" placement="right" width="500" v-model="visible">
        <ElxImg @pic="navPic"></ElxImg>
        <span class="idp-custom-emoticon four cont" slot="reference" style="color: #3370ff">选择表情</span>
      </el-popover>
      <!-- 发布 -->
      <el-button type="primary" @click="postComments" class="fr">发表评论</el-button>
    </div>
  </div>
</template>

<script>
import ElxImg from "./Image.vue";
import Api from "@/api/XXApi";

export default {
  components: {
    ElxImg
  },
  props: {},
  watch: {},
  computed: {},
  data() {
    return {
      lock: true,
      fullContent: "",
      visible: false,
      imgArr:[],
    };
  },
  created() {
    const clearSelection = () => {
      if (document.selection) {
        document.selection.empty();
      } else {
        document.getSelection().removeAllRanges();
      }
    };
    // 禁用右键contextmenu、copy、select、drag 等相关事件
    Array.from(document.querySelectorAll("img")).forEach((img) => {
      [
        "contextmenu",
        "select",
        "selectstart",
        "copy",
        "beforecopy",
        "dragstart",
        "mouseup",
      ].forEach((type) => {
        img["on" + type] = () => {
          clearSelection();
          return false;
        };
      });
    });
    // 不允许全选,可以选择部分元素;离开页面记得释放事件
    window.onkeydown = function (e) {
      if (
        e.keyCode === 65 &&
        (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey)
      ) {
        e.preventDefault();
        clearSelection();
        return false;
      }
    };
  },
  methods: {
    navPic(val) {
      let content = document.getElementById('content'); //内容
      var imgNode = document.createElement('img');  //图片
      let numText = document.getElementById("num");  //总数样式
      let totalText = document.getElementById("total");  //总数
      let innerLength = 0;
      // 什么条件下可以添加表情包
      if(totalText.innerText < 100){
        imgNode.src = val;
        content.appendChild(imgNode);
        // 共输入几个表情包
        this.imgArr = content.innerHTML.match(/<img.*?>/g);
        // 输入的表情包占多少字符串(一个表情占据21个字符串)
        if(this.imgArr && this.imgArr.length > 0){
          innerLength = 21 * this.imgArr.length;
          totalText.innerText = (content.innerHTML.length - innerLength) + (this.imgArr.length * 5);
        }else{
          totalText.innerText = content.innerHTML.length;
        }
      }else{
        content.style.borderColor = "red";
        numText.style.color = "red";
      }
      // this.keepCursorEnd()
      // this.keepCursorEnd(event.target);
    },
    // 字数限制
    addInput(event) {
      let numText = document.getElementById("num");
      let totalText = document.getElementById("total");
      let content = document.getElementById("content");
      let _words = content.innerText;
      if (this.lock) {
        let num = _words.length;
        if (num >= 100) {
          num = 100;
          if (
            event.target.style.borderColor == ("red" || "rgb(205, 205, 205)")
          ) {
            event.target.innerText = this.fullContent;
          } else {
            event.target.innerText = _words.substring(0, 100);
            content.style.borderColor = "red";
            numText.style.color = "red";
            this.fullContent = _words.substring(0, 100);
          }
          this.keepCursorEnd(event.target);
        } else {
          content.style.borderColor = "#CDCDCD";
          numText.style.color = "#CDCDCD";
          this.fullContent = "";
        }
        totalText.innerText = num;

        // 输入的内容中,表情包占多少字符串(一个表情占据21个字符串)
        if(content.innerHTML){
          let newText = (totalText.innerText) * 1;
          this.imgArr = content.innerHTML.match(/<img.*?>/g);
          if(this.imgArr && this.imgArr.length) totalText.innerText = newText + (this.imgArr.length * 5);
        }
        
      } else if (this.fullContent) {
        // 目标对象:超过100字时候的中文输入法
        // 原由:虽然不会输入成功,但是输入过程中字母依然会显现在输入框内
        // 弊端:谷歌浏览器输入法的界面偶尔会闪现
        event.target.innerText = this.fullContent;
        this.lock = true;
        this.keepCursorEnd(content.innerHTML);
      }
    },
    // 发布
    postComments() {
      var content = document.getElementById('content');
      if(content.innerHTML){
        // 正则表达式匹配<img src="/web/e/11.gif">标签
        const regex = /<img src="\/e\/(\d+)\.gif">/g;
        // 替换标签为${11}的形式,实现每个表情占5个字符(这个可以根据需求来自定义)
        const replacedHtml = content.innerHTML.replace(regex, '${$1}');

        Api.commentSubmit({
            commentId: 0,
            content: replacedHtml,
            replyUid: 0,
            sourceId : this.contentId,
            sourceType: 'cms',
        })
        .then((res) => {
            this.$notifySuccess('评论成功');
            this.initData();
            if(this.$refs.comment) this.$refs.comment.innerText = '';
            totalText.innerText = 0;
        });
      }
    },
    // 如果需求要求超过字数后还可以中间输入内容,
    // 请忽略掉fullContent有关的地方,主要位置addInput()

    // 中文输入法问题(如果是禁止输入空格的需求,需用此方法,针对Firefox浏览器中五笔输入法,刚敲键盘,焦点会自动空一格,选择输入内容空格才会消失,直接oninput禁止输入空格会导致五笔输入法无法输入中文)
    navCompositionstart() {
      this.lock = false;
    },
    navCompositionend(event) {
      this.lock = true;
      this.addInput(event);
    },
    navKeyup(event) {
      this.addInput(event);
    },
    navInput(event) {
      this.addInput(event);
    },
    navPropertychange(event) {
      this.addInput(event);
    },
    navFocus() {
      let str = this.$refs.comment;
      this.keepCursorEnd(str);
    },
    // 监听粘贴div(contenteditable = "true")富文本转为纯文本对内容进行处理
    optimizePasteEvent(e) {
      e.stopPropagation();
      e.preventDefault();
      let text = "",
        event = e.originalEvent || e;
      if (event.clipboardData && event.clipboardData.getData) {
        text = event.clipboardData.getData("text/plain");
      } else if (window.clipboardData && window.clipboardData.getData) {
        text = window.clipboardData.getData("text");
      }

      if (document.queryCommandSupported("insertText")) {
        document.execCommand("insertText", false, text);
      } else {
        document.execCommand("paste", false, text);
      }
    },
    // 定位div(contenteditable = "true");超过字数光标定位到末端 将光标重新定位到内容最后
    keepCursorEnd(obj) {
      // ie11 10 9 firefox safari
      if (window.getSelection) {
        // 解决firefox不获取焦点无法定位问题
        obj.focus();
        // 创建range
        let range = window.getSelection();
        // range 选择obj下所有子内容
        range.selectAllChildren(obj);
        // 光标移至最后
        range.collapseToEnd();
      } else if (document.selection) {
        //ie10 9 8 7 6 5
        // 创建选择对象
        let range = document.selection.createRange();
        //range定位到obj
        range.moveToElementText(obj);
        //光标移至最后
        range.collapse(false);
        range.select();
      }
    },
  },
  mounted() {},
};
</script>

<style lang="scss" scoped>
  /* 输入框 */
.dzm-textarea {
  background: none;
  outline: none;
  padding: 10px 10px 30px;
  border: 1px solid #eeeeee;
  border-radius: 4px;
  word-wrap: break-word;
  word-break: break-all;
  -webkit-user-modify: read-write-plaintext-only;
}
/* 输入框为空时显示 placeholder */
.dzm-textarea:empty:before {
  content: attr(placeholder);
  color: #cdcdcd;
}
/* 输入框获取焦点时移除 placeholder */
.dzm-textarea:focus:before {
  // content: none;
  line-height:18px;
}
[contenteditable]:focus{
  outline: none;
  border: 1px solid #5d84e9; 
}
.main-num {
  position: absolute;
  bottom: 10px;
  right: 15px;
  color: #cdcdcd;
  line-height: 16px;
  text-align: right;
}
.content {
  .topTitle {
    margin-top: 20px;
    .two {
      color: #999999;
      font-size: 12px;
    }
  }
  .editTextList{
    position:relative;
    .editText {
      min-height: 100px;
      margin-top: 10px;
      background-color: #ffffff;
      padding-bottom:20px;
      &:after {
        color:#333333;
        line-height: 18px;
      }
      /deep/img{
        width: 22px; 
        height: 22px; 
        margin-right: 2px;
        vertical-align: middle;
        display: inline-block;
        position: relative;
        top:-2px;
        // 处理右键不可复制图片
        -webkit-user-drag: none;
				-khtml-user-drag: none;
				-moz-user-drag: none;
				-o-user-drag: none;
				user-drag: none;
				-webkit-user-select: none;
				-khtml-user-select: none;
				-moz-user-select: none;
				-o-user-select: none;
				user-select: none;
				pointer-events: none;
      }
    }
    .numTotal{
      position:absolute;
      bottom:10px;
      right:10px;
      color: #333333;
    }
  }
 
  .three {
    margin-top: 15px;
    overflow: hidden;

    .four {
      margin-top: 20px;
    }
  }
  .list {
    overflow: hidden;
    padding-bottom: 15px;
    .box {
      margin: 15px 0 10px 0;
      overflow: hidden;
      border-bottom: 1px solid #eeeeee;
      padding-bottom: 20px;
      .img {
        width: 50px;
        height: 50px;
        border-radius: 50%;
      }
      .userName{
            width:50px;
            height:50px;
            line-height:50px;
            text-align: center;
            color: #ffffff;
            font-size:14px;
            background: #5d84e9;
            border-radius: 50%;
        }
    }
  }
  .cont:hover {
    cursor: pointer;
  }
  .navBox {
    background: #f6f6f6;
    padding: 5px 10px;
    margin-top: 10px;
    overflow: hidden;
  }
}
.navText{
    margin-top:10px;
    overflow: hidden;
    line-height: 18px;
    color:#333333;
    word-wrap: break-word;  //换行
    /deep/img {
      width: 22px !important; 
      height: 22px !important; 
      // 右键不允许复制
      -webkit-user-drag: none;
      -khtml-user-drag: none;
      -moz-user-drag: none;
      -o-user-drag: none;
      user-drag: none;
      -webkit-user-select: none;
      -khtml-user-select: none;
      -moz-user-select: none;
      -o-user-select: none;
      user-select: none;
      pointer-events: none; 
    }
}
.huifu{
  width:99%;
  font-size:12px;
  overflow: hidden;
  float:right;
  margin-bottom:10px;
}
.hui{
  width:93%;
}
.foot{
  width:100%;
  color: #6980ff;
  font-size: 12px;
  padding-bottom: 20px;
  margin-top:20px;
  transform: all 3s;
  overflow: hidden;
}
.el-button--whiteBackground:focus,
.el-button--whiteBackground:hover {
  background: none;
  border-color: none;
  color: none;
}
.el-button--whiteBackground.is-active,
.el-button--whiteBackground:active {
  background: none;
  border-color: none;
  color: none;
}
.textDialog{
  color:#FF3333;
  margin-top:5px;
  font-size: 13px;
}
img {
    -webkit-user-drag: none;
    -khtml-user-drag: none;
    -moz-user-drag: none;
    -o-user-drag: none;
    user-drag: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -o-user-select: none;
    user-select: none;
    pointer-events: none;
  }
</style>

以下代码块为表情包(多个图片组成的图片库)

<template>
  <div class="content">
    <div class="content">
       <img :src="item" alt="" v-for="(item,index) in imgSrc" :key="index" @click="selImg(item)">
    </div>
  </div>
</template>

<script>
export default {
    mixins:[],
    components:{},
    props:{},
    watch: {},
    computed: {
        imgSrc(){
            let arr = [];
            for (let i = 10; i <= 86; i++) {
                arr.push(__static__ + `/e/${i}.gif`);
            }
            return arr
        },
    },
    data() {
        return {
            cont:'',
        }
    },
    created() {},
    mounted() {},
    methods: {
        selImg(val){
            this.cont = val;
            this.$emit('pic', this.cont);
        }
    },
}
</script>
<style lang='scss' scoped>
</style>

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/791989.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[c++实验] 快读快写,O123优化,原版用时对比

前言 学过c的多知道&#xff0c;准确的来说是做过c题目的都知道&#xff1a;c题目不仅要求代码正确&#xff0c;还要求用时&#xff0c;大多用时要求都在200ms--1000ms之间&#xff0c;要是遇到大数据时&#xff0c;超时的可能就会大大提升。 结论 用时的把控也很重要&#…

网站实现下载apk安装包

目录 1、背景说明2、效果图3、具体实现3.1 界面代码3.2 js代码 4、说明4.1 存在异常4.2 解决方案 5、参考资料 1、背景说明 有时需要将写好的apk安装包在局域网内部进行发布&#xff0c;具体实现非常简单&#xff0c;如下所示 2、效果图 进入到网站后&#xff0c;点击下载按…

正确认识:2374782-02-0,FAPI-4,成纤维细胞活化蛋白抑制剂

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ PART1----试剂基础信息​​ 【中文名称】成纤维细胞活化蛋白抑制剂 【英文名称】 FAPI-4 【结 构 式】 【CAS】2374782-02-0 【分子式】C40H54F2N10O10 【分子量】872.93 【沸点】1144.165.0 C(Predicted) 【密度】1.460.…

【力扣周赛】第 355 场周赛(构造二分答案异或前缀 状态压缩⭐)

文章目录 Q1&#xff1a;6921. 按分隔符拆分字符串&#xff08;双指针&#xff09;Q2&#xff1a;6915. 合并后数组中的最大元素&#xff08;倒序遍历贪心&#xff09;代码优化 Q3&#xff1a;6955. 长度递增组的最大数目&#x1f6b9;&#x1f6b9;&#x1f6b9;&#x1f6b9;…

企业级PaaS低代码快开平台源码,基于 Salesforce Platform 的开源替代方案

PaaS低代码快开平台是一种快速开发应用系统的工具&#xff0c;用户通过少量代码甚至不写代码就可以快速构建出各种应用系统。 随着信息化技术的发展&#xff0c;企业对信息化开发的需求正在逐渐改变&#xff0c;传统的定制开发已经无法满足企业需求。低代码开发平台&#xff0…

ElementUI tabs标签页样式改造美化

今天针对ElementUI的Tabs标签页进行了样式修改&#xff0c;更改为如下图所属的样子。 在线运行地址&#xff1a;JSRUN项目-ElementUI tabs标签页样式改造 大家如果有需要可以拿来修改使用&#xff0c;下面我也简单的贴上代码&#xff0c;代码没有注释&#xff0c;很抱歉&#x…

Centos7.9安装瀚高数据库企业版6.0.4_并开启远程连接_使用瀚高连接工具操作_亲测成功---国产瀚高数据库工作笔记003

1.首先去瀚高官网,注册,然后下载安装包, OpenEuler23.03欧拉系统_安装瀚高数据库企业版6.0.4_openeuler切换root用户_su:拒绝权限_passwd: 鉴定令牌操作错误---国产瀚高数据库工作笔记001 一部分内容可以参考一下这个博文 2.准备一下环境: 关闭防火墙,关闭网络管理器 syste…

Spingboot yaml 配置文件及数据读取

属性配置在这里插入图片描述 修改服务器端口 → server.port80 修改 banner → spring.main.banner off(关闭)/console(控制台)/log(日志) 日志 → logging.level.rootinfo Common Application Properties 配置文件分类 优先级 如果三种文件共存时&#xff0c;优先级为&am…

618技术揭秘 - 大促弹窗搭投实践 | 京东云技术团队

背景 618 大促来了&#xff0c;对于业务团队来说&#xff0c;最重要的事情莫过于各种大促营销。如会场、直播带货、频道内营销等等。而弹窗作为一个极其重要的强触达营销工具&#xff0c;通常用来渲染大促氛围、引流主会场、以及通过频道活动来提升频道复访等。因此&#xff0…

基本数据类型转换(基本数据类型之间的运算规则)

自动类型转换 前提&#xff1a;这里讨论只是7种基本数据类型变量间的运算。不包含boolean类型的。 自动类型转换&#xff1a;容量小的类型自动转换为容量大的数据类型。数据类型按容量大小排序为&#xff1a; 有多种类型的数据混合运算时&#xff0c;系统首先自动将所有数据 …

ssm文章发布管理系统java小说作品发表jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 ssm文章发布管理系统 系统有2权限&#xff1a;前台账…

企业微信开发应用之获取userId

一、创建应用 企业微信 (qq.com) 在【应用管理】界面-【应用】-【自建】点击【创建应用】 参考 一&#xff1a;如何创建企业内部应用 - 教程 - 企业微信开发者中心 (qq.com) 二、配置信息 开发阶段“应用可见范围”可先选择小范围可见&#xff0c;待开发完成后再开放给企业…

PMP成绩查询及电子版证书下载方式

2023年05月27日PMP考试成绩预计将于2023年7月24日晚开始发布&#xff0c;按照往年的情况&#xff0c;成绩都是分批出的&#xff0c;如果暂时没查到成绩的同学请耐心等待&#xff0c;预计一周内成绩会全部出来。 具体查询方法如下 当你在PMI的注册邮箱收到一封PMI发来的&#x…

EC200U-CN学习(四)

EC200U系列内置丰富的网络协议&#xff0c;集成多个工业标准接口&#xff0c;并支持多种驱动和软件功能&#xff08;适用于Windows 7/8/8.1/10、Linux和Android等操作系统下的USB驱动&#xff09;&#xff0c;极大地拓展了其在M2M领域的应用范围&#xff0c;如POS、POC、ETC、共…

JavaScript学习 -- Base64编码

Base64编码是一种常用的将二进制数据转换为文本数据的方式。在JavaScript中&#xff0c;我们可以通过使用Base64编码算法&#xff0c;将二进制数据转换为可读的文本数据&#xff0c;以便于在网络传输、文件传输等场景下使用。在本篇博客中&#xff0c;我们将介绍Base64编码的基…

pipeline和retiming

首先,pipeline 是 rtl design 的技巧,Retiming 是 synthesize 的技术。设计里面要有pipeline,才有后面的retiming。 当然,現在synthesis 进步很多了,這句话在有些时候已经不成立了,但是,大多数的时候,Retiming 还是针对pipeline 做优化。 一個简单的例子,例如我们做一…

021 - count()函数 - 对结果进行计数统计

COUNT() 函数返回匹配指定条件的行数。 -- 语法&#xff1a; COUNT(column_name) 函数返回指定列的值的数目&#xff08;NULL 不计入); SELECT COUNT(name) AS "统计" FROM test02; -- 语法&#xff1a; COUNT(*) 函数返回表中的记录数; select COUNT(*) AS "统…

【沁恒蓝牙mesh】手机配网+自组网联合调试

开发蓝牙mesh过程中&#xff0c;需要一个管理者&#xff0c;采用以下方案 一个节点用手机配网的方式&#xff0c;其余节点用自组网的方式&#xff0c;只要手机组网的节点与自组网的节点的配网信息相同&#xff0c;所有节点就可以在一个mesh网络中。 1. 组网描述 描述&#xff1…

【外卖系统】新增员工

需求分析和数据模型 新增员工就是将新增页面录入的员工数据插入到emoloyee表&#xff0c;username字段约束是唯一的&#xff0c;即员工的登录账号是唯一的status字段默认值为1&#xff0c;表示状态正常 前端界面 报错信息&#xff1a; 代码开发分析 页面发送ajax请求&…

推荐几款不错的AI绘画工具

随着近年来数据、算法等核心技术的不断进步&#xff0c;人工智能在内容创作各垂直领域的比例不断增加&#xff0c;包括人工智能写作、人工智能编辑和最近流行的人工智能绘画。 许多朋友也想跟上潮流&#xff0c;使用人工智能绘画生成软件创建人工智能图像&#xff0c;但我不知…