【Vue】div标签实现输入框,利用contenteditable=“true“属性的标签实现
【代码】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){
//这块主要是处理表情包,表情包正常下占21个字符或24个字符,可以转化成${11.gif},让所有表情包统一占5个字符,根据需求可以自定义
// 正则表达式匹配<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;
}
}
}
.three {
margin-top: 15px;
overflow: hidden;
.four {
margin-top: 20px;
}
}
.cont:hover {
cursor: pointer;
}
}
.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;
}
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>
更多推荐
已为社区贡献2条内容
所有评论(0)