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

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

使用 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>

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT如何帮助学生学习

​ 一些教育工作者担心学生可能使用ChatGPT作弊。因为这个AI工具能写报告和计算机代码&#xff0c;画出复杂图表……甚至已经有许多学校把ChatGPT屏蔽。 研究发现&#xff0c;学生作弊的主要原因是想考得好。是否作弊与作业和考试的打分方式有关&#xff0c;所以这与技术的便…

《零基础入门学习Python》第062讲:论一只爬虫的自我修养10:安装Scrapy

这节课我们来谈谈 Scrapy 说到Python爬虫&#xff0c;大牛们都会不约而同地提起Scrapy。因为Scrapy是一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架。可以应用在包括数据挖掘&#xff0c;信息处理或存储历史数据等一系列的程序中。 Scrapy最初是为了页面抓…

2023年发布的25个开源大型语言模型总结

大型语言模型(llm)是一种人工智能(AI)&#xff0c;在大量文本和代码数据集上进行训练。它们可以用于各种任务&#xff0c;包括生成文本、翻译语言和编写不同类型的创意内容。 今年开始&#xff0c;人们对开源LLM越来越感兴趣。这些模型是在开源许可下发布的&#xff0c;这意味…

HCIA练习2

目录 第一步 启动eNSP&#xff0c;搭建如图所示的拓扑结构 第二步 进行子网的划分 ​第三步 从第二步划分的16个网段中&#xff0c;选择14个网段进行使用 第四步 对路由器各个端口进行IP配置 第五步 对每个路由器的环回接口进行配置 第六步 对路由器进行静态路由配…

Redis 命令介绍

文章目录 Redis字符串操作命令哈希操作命令列表操作命令set集合sorted set 有序集合通用命令 在Java中操作Redis&#x1f350; ❤️ &#x1f6a9;4.1 Redis的Java客户端 &#x1f350;4.2 Spring Data Redis使用方式 ✏️环境搭建步骤1). 导入Spring Data Redis的maven坐标2).…

基于SpringBoot+Vue的“智慧食堂”系统设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

ARM寄存器组织

一、寄存器二、ARM寄存器三、专用寄存器四、CPSR寄存器 一、寄存器 概念 寄存器是处理器内部的存储器&#xff0c;没有地址 作用 一般用于暂时存放参与运算的数据和运算结果 分类 包括通用寄存器、专用寄存器、控制寄存器 二、ARM寄存器 注 在某个特定模式下只能使用当前模…

Mac m1 下eclipse下载及jdk环境变量配置

一、安装eclipse 1、下载eclipse Eclipse downloads - Select a mirror | The Eclipse Foundation 此版本为m1芯片适用版本 2、下载后下一步安装即可 安装成功后&#xff0c;可以看到图标&#xff1a; 二、安装jdk 1、下载jdk 下载此版本即可&#xff0c;下载完成之后一直…

Django模型将模型注释同步到数据库

1、安装django-comment-migrate库 pip install django-comment-migrate 2、将库注册到settings.py文件中 INSTALLED_APPS [...django_comment_migrate, # 表注释... ] 3、加注释 3.1、给模型&#xff08;表&#xff09;加注释 在模型的class Meta中编辑 verbose_name&…

Spring Cloud+Spring Boot+Mybatis+uniapp+前后端分离实现知识付费平台免费搭建

Java版知识付费-轻松拥有知识付费平台 多种直播形式&#xff0c;全面满足直播场景需求 公开课、小班课、独立直播间等类型&#xff0c;满足讲师个性化直播场景需求&#xff1b;低延迟、双向视频&#xff0c;亲密互动&#xff0c;无论是互动、答疑&#xff0c;还是打赏、带货、…

【Postman】Newman安装与环境配置完整版(内含安装过程中遇到的问题与解决方案)

文章目录 概要Newman安装三步走一、nodejs安装与环境配置1、安装2、环境配置 二、安装newman1、步骤2、问题与解决方案 三、安装newman-reporter-html 概要 Newman&#xff1a;一款基于nodejs开发的可以运行Postman脚本的工具&#xff0c;并且可以生成测试报告。本文介绍了New…

【docker】docker部署mysql

目录 一、步骤二、说明三、示例 一、步骤 1.搜索mysql镜像 2.拉取mysql镜像 3.创建容器 4.操作容器中的mysql 二、说明 1.容器内的网络服务和外部机器不能直接通信 2.容器中部署的mysql端口3306不能被外部机器和宿主机直接通信 3.外部机器和宿主机之间可以直接通信 4.宿主机和…

管理类联考——数学——趣味篇——可视化

Manim: 一个数学可视化的动画引擎 官网&#xff1a;https://3b1b.github.io/manim/index.html 名词解析 python3.7是python语言的解释器, 运行python程序的环境必备品. 这个没啥说的,大家都能懂. 虽然官方建议3.7,但是我用3.8发现也没问题.考虑未来的历史进程,大伙最好还是装…

力扣题库刷题笔记75--颜色分类

1、题目如下&#xff1a; 2、个人Pyhon代码实现如下&#xff1a; 第一种思路是取巧&#xff0c;通过计数0、1、2的个数&#xff0c;去替换nums 备注第10行代码在本地可以跑过&#xff0c;但是力扣跑不过&#xff0c;所以就用了第10-16行代码进行替换 第二种思路是通过冒泡排序去…

小程序创建

1&#xff0c;下载HBuilder X ;(3.8.7) HBuilderX-高效极客技巧 2,下载模板&#xff08;不选云服务的&#xff09;&#xff1b; 3&#xff0c;运行-运行到小程序模拟器&#xff1b; 4&#xff0c;安装小程序开发工具&#xff1b; 5&#xff0c;选择稳定版-windows64版&…

springboot集成

maven配置 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency><groupId>org.apache.commons</groupId><artifactId>…

【GPT4结对编程】word文档导出功能GPT4来实现

需求背景 最近产品增加了一个导出word文档的需求&#xff0c;之前有导出过pdf格式、excel格式、csv格式&#xff0c;但还没导出过word文档。 开源框架调研 我们的后端服务主要是用golang&#xff0c;因此首先想到的是golang相关的开源工具&#xff0c;找到2个。 unioffice …

第二天 kali代理配置

文章目录 环境一、虚拟机网络模式&#xff08;1&#xff09;NAT&#xff08;2&#xff09;NAT模式&#xff08;3&#xff09;桥接模式&#xff08;4&#xff09;仅主机模式&#xff08;5&#xff09;总结 二、配置代理&#xff08;桥接模式&#xff09;1、基础设置2、虚拟机浏览…

【iOS】Cocoapods的安装以及使用

文章目录 前言一、Cocoapods的作用二、安装Cocoapods三、使用Cocoapods总结 前言 最近笔者在仿写天气预报App时用到了api调用数据&#xff0c;一般的基本数据类型我们用Xcode中自带的框架就可以转换得到。但是在和风天气api中的图标的格式为svg格式。 似乎iOS13之后Xcode中可…

kafka面试题

kafka基本概念 Producer 生产者&#xff1a;负责将消息发送到 BrokerConsumer 消费者&#xff1a;从 Broker 接收消息Consumer Group 消费者组&#xff1a;由多个 Consumer 组成。消费者组内每个消费者负责消费不同分区的数据&#xff0c;一个分区只能由一个组内消费者消费&am…