js 带表情的评论输入框问题
<div class="comment_con clearfix">
<div class="comment_input_box">
<!-- div 模拟input输入框 -->
<div type="text" class="comment_input" contenteditable="true" v-on:input="inputChange($event)" v-on:paste="inputPaste($event)" v-on:focus.stop="inputFocus($event)"></div>
<!--表情按钮 -->
<input type="button" class="emoji-wrap" @click.stop="showEmijiBtn($event,1)" />
</div>
<span class="comment_btn" @click="sendComment($event)">评论</span>
<!--表情弹窗 -->
<div class="dynamic-emoji" v-show="isEmojiShow">
<div id="emoji-box" class="emoji-box open">
<span class="td-div" v-for="(value,key) in arrEmojiList" @click="selectEmoji($event,value,key)">
<img class="emoji" :src="emoji_path+value" :title="key"/>
</span>
</div>
</div>
</div>
1.输入框粘贴处理
inputPaste: function(e) {
var e = e || window.event;
var clipboar = e.clipboardData || e.originalEvent.clipboardData;
if (clipboar) {
var text = clipboar.getData('Text');
if (text.length > 140) {
text = text.substr(0, 140);
this.dialogShowFn(2, '粘贴内容过长,已截取前140字!');
}
String.insertAtCaret(this.curElementInput, text);
e.preventDefault();
}
},
2.控制字数 内部文字和表情个数
if (this.curElementInput.innerText.length + this.curElementInput.childElementCount > 140) {
this.dialogShowFn(2, "评论内容不能超过140字");
return false;
}
3.转换表情
readEmoji: function(message) {
//替换信息中的 < & 空格
var message = this.convertHtml(message, true);
//如果有at信息,特殊显示
//提取消息中的图片信息
message = message.replace(/\[[^\[\]]+\]/g, function (v, i, s) { return this.arrEmojiList[v] ? ('<img class="emoji" src="' + this.emoji_path + this.arrEmojiList[v] + '">') : v }.bind(this));//新表情的解析
return message;
},
4.特殊字符转换
convertHtml: function(msg, flag) {
msg = msg + ""; //如果是数字,转为字符串
if (flag) {
msg = msg.replace(/&/g, '&').replace(/\s/g, ' ').replace(/</g, '<').replace(/>/g, '>');
} else {
msg = msg.replace(/>/g, '>').replace(/</g, '<').replace(/ /g, ' ').replace(/&/g, '&');
}
return msg;
},
5.设置光标位置
setFocus: function(el) {
var range = document.createRange();
},
6.插入字符串
// 字符串解析为DOM对象
function loadXMLString(str) {
var div = document.createElement("div");
if(typeof str == "string") div.innerHTML = str;
return div.childNodes;
};
// 插入字符串DOM
function insertAtCaret(id, val) {
//光标位置 光标在表情前面--上一个文本的结尾 光标在表情后面--输入框子节点的序号
var selection = window.getSelection && window.getSelection();
var anchorNode = selection.anchorNode;
var anchorOffset = selection.anchorOffset;
//输入框
// var pn = document.getElementById(id);
var pn = id;
//选区
var ran = selection.getRangeAt(0);
//插入的表情
var imgNode = loadXMLString(val)[0];
if (anchorNode.nodeType == 3) {
//文本中插入
// if (selection.anchorOffset == anchorNode.nodeValue.length){
// //在文本结尾
// pn.insertBefore(imgNode ,anchorNode.nextElementSibling);
// } else
if (selection.anchorOffset == 0){
//在文本开头
pn.insertBefore(imgNode ,anchorNode);
} else {
//在文本中
//拆分后半部分文本并插入
var n2 = document.createTextNode(anchorNode.nodeValue.substring(selection.anchorOffset));
pn.replaceChild(n2, anchorNode);
//在后半部分文本前插入表情
imgNode = pn.insertBefore(imgNode, n2);
//插入前半部分文本
if(anchorOffset){
var n1 = document.createTextNode(anchorNode.nodeValue.substring(0, anchorOffset));
pn.insertBefore(n1, imgNode);
}
}
}else if(anchorNode.nodeType == 1){
//输入框子节点插入
//在选中节点的后面一个节点,在它之前插入
pn.insertBefore(imgNode, pn.childNodes[anchorOffset]);
}
//结尾表情被隐藏处理176
if (imgNode.offsetLeft - pn.scrollLeft > 176){
pn.scrollLeft = imgNode.offsetLeft - 176;
}
//设置焦点
ran = ran.cloneRange();
ran.setStartAfter(imgNode);
selection.removeAllRanges();
selection.addRange(ran);
pn.focus();
};
转自https://www.cnblogs.com/yuesu/p/9408582.html
div 模拟输入框
1.默认分享文案:发现一个好玩的歌房,快来一起玩吧~ ,获取鼠标时,默认文案消失
利用 empty,focus,before 伪元素解决,模拟placeholder
<div class="editBox" contenteditable="true" placeholder="发现一个好玩的歌房,快来一起玩吧~"></div>
.editBox:empty:before{ content: attr(placeholder); color:#999; } .editBox:focus:before{ content:none; }
2.最多输入140个汉字 ,已输入文字数量随输入实时变化。超出140汉字时,不能输入进去
问题:
1.输入框粘贴,去掉格式,截取字数
2.超过140字时,再次输入,光标总是默认在最前面,需要记录光标的位置
// 获取输入框内容,先记录光标的位置,截取140字后,重新设置光标位置 inputChange(e) { this.cursorPos=this.getCursorPosition(e.currentTarget); if(e.currentTarget.innerText.length > 140){ this.shareTipFn(1,"内容不能超过140字"); e.currentTarget.innerText=e.currentTarget.innerText.substr(0,140); this.setCursorPosition(e.currentTarget,Math.min(this.cursorPos,140)); } this.content=e.currentTarget.innerText; this.curTextLen=this.content.length; }, // 输入框粘贴处理 inputPaste(e) { e.preventDefault(); var clipboar = e.clipboardData || e.originalEvent.clipboardData; if (clipboar) { var text = clipboar.getData('Text'); String.insertAtCaret(e.currentTarget, text); this.inputChange(e); } }, //获取当前光标位置 getCursorPosition (element) { var caretOffset = 0; var doc = element.ownerDocument || element.document; var win = doc.defaultView || doc.parentWindow; var sel = win.getSelection(); if (sel.rangeCount > 0) {//选中的区域 var range = win.getSelection().getRangeAt(0);//获取指定的选中区域 var preCaretRange = range.cloneRange();//克隆一个选中区域 preCaretRange.selectNodeContents(element);//设置选中区域的节点内容为当前节点 preCaretRange.setEnd(range.endContainer, range.endOffset); //重置选中区域的结束位置 caretOffset = preCaretRange.toString().length; } console.log(caretOffset,sel.focusOffset); return caretOffset; }, // 设置光标位置 setCursorPosition(element, pos){ var range = document.createRange();//创建一个选中区域 range.selectNodeContents(element);//选中节点的内容 if(element.innerHTML.length > 0) { range.setStart(element.childNodes[0], pos); //设置光标起始为指定位置 } range.collapse(true); //设置选中区域为一个点 var selection = window.getSelection();//获取当前选中区域 selection.removeAllRanges();//移出所有的选中范围 selection.addRange(range);//添加新建的范围 },
3.点击分享,
1.设置flag标记,防止频繁点击
2.判断有无网
div 模拟输入框,支持输入表情
1.html结构
<div id="app" v-clock @click="emojiClose"> <div class="editBox" > <div class="edit" contenteditable="true" placeholder="发现一个好玩的歌房,快来一起玩吧~" v-on:input="inputChange($event)" v-on:keydown="inputkeydown($event)" v-on:paste="inputPaste($event)" ></div> <div class="editBottom"> <img class="emojiImg" src="../images/emoji-icon.png" @click.stop="emojiBtn"/> <div class="textLen"><span class="curLen">{{curTextLen}}</span>/140</div> </div> <!--表情框 --> <div class="emojiBox" v-show="isEmojiShow"> <div class="emojiList"> <span class="emojiImg" v-for="(value,key) in arrEmojiList" :title="key" @click.stop="selectEmoji($event,value,key)"> <img :src="emoji_path+value"> </span> </div> </div> </div> <div class="shareBtn" :class="flag?'shareBtnRed':'shareBtnGray'" @click="shareFn">分享</div> <!--分享提示 弹层 --> <div class="tipBox" v-show="shareTip.isShow"> <div class="tipImg" :class="shareTip.type==0?'success':'warn'"></div> <span>{{shareTip.text}}</span> </div> </div>
2.默认分享文案:发现一个好玩的歌房,快来一起玩吧~ , 获取鼠标时,默认文案消失
.edit:empty:before{ content: attr(placeholder); color:#999; } .edit:focus:before{ content:none; }
2.最多输入140个汉字,支持输入表情
屏蔽回车,粘贴处理,表情处理
// 屏蔽回车 inputkeydown(event){ if (window.event && window.event.keyCode == 13) { event.cancelBubble=true; event.preventDefault(); event.stopPropagation(); // this.shareFn(); } }, // 获取输入框内容 inputChange(e) { let curTarget=document.getElementsByClassName('edit')[0]; this.getCursorPosition(); if(curTarget.innerText.length + curTarget.childElementCount > 140){ this.shareTipFn(1,"内容不能超过140字"); // 图片:移除最后一个元素 文字 if((/<[a-z|/]+>$/).test(curTarget.innerHTML)){ curTarget.removeChild(curTarget.lastElementChild); }else{ let imgArr=curTarget.innerHTML.match(/<img.*?data-id="(.*?)"\/?>/ig)||[]; let imgLen = imgArr.join('').length-imgArr.length; // curTarget.innerHTML=curTarget.innerHTML.substr(0,140+imgLen); curTarget.innerHTML=curTarget.innerHTML.substr(0,curTarget.innerHTML.length-(curTarget.innerText.length + curTarget.childElementCount-140)); } this.setCursorPosition(curTarget); } this.curTextLen=curTarget.innerText.length + curTarget.childElementCount; this.content=curTarget.innerHTML; }, // 输入框处理 inputPaste(e) { e.preventDefault(); var clipboar = e.clipboardData || e.originalEvent.clipboardData; if (clipboar) { var text = clipboar.getData('Text'); String.insertAtCaret(e.currentTarget, text.replace(/\s/g,'')); this.inputChange(e); } }, //获取当前光标位置 getCursorPosition () { var section = window.getSelection(); this.sectionObj.anchorNode = section.anchorNode; this.sectionObj.anchorOffset =section.anchorOffset; }, // 设置光标位置 setCursorPosition(element){ var range = document.createRange(); if(this.sectionObj.anchorNode.nodeType == 1){ //焦点在图片 range.setStart(element,Math.min(this.sectionObj.anchorOffset,element.childNodes.length)); }else{ //焦点在文本 let node=this.sectionObj.anchorNode; // for(let i=0;i<element.childNodes.length;i++){ // if((element.childNodes[i].data&&node.data==element.childNodes[i].data)||i==element.childNodes.length-1){ // range.setStart(element.childNodes[i],Math.min(this.sectionObj.anchorOffset,element.childNodes[i].length)); // break; // } // } range.setStart(node,Math.min(this.sectionObj.anchorOffset,node.length)); } range.collapse(true); //设置选中区域为一个点 var selection = window.getSelection();//获取当前选中区域 selection.removeAllRanges();//移出所有的选中范围 selection.addRange(range);//添加新建的范围 }, // 选择表情 selectEmoji(e,value,key) { let curElement=document.getElementsByClassName('edit')[0]; curElement.focus(); let str = '<img class="emojiImg" src="' +this.emoji_path+value + '" data-id="' + key + '"\/>'; if(e.target.nodeName == 'IMG') { String.insertAtCaret(curElement,str); this.inputChange(e); } this.isEmojiShow=false; }, // 表情按钮 显示 隐藏 emojiBtn(){ this.isEmojiShow=!this.isEmojiShow; },
4.分享操作
分享内容:对表情,特殊字符处理
点击分享:防止频繁点击操作,判断有无网
// 分享 shareFn(){ if(!this.flag) return; this.flag=false; // 处理表情图片 this.content=this.content.replace(/<img.*?data-id="(.*?)"\/?>/ig, '$1'); this.content=this.convertHtml(this.content,false); var params={ userID: this.userID, toUserID:this.toUserID, objectID:this.objectID, content:this.content||this.shareDefaultText, type: this.type } params=encodeURIComponent(JSON.stringify(params)); axios.get('https://music.51vv.com/sod?parameter='+params) .then(res=>{ if(res.data.retCode==1000){ this.shareTipFn(0,'分享成功!'); }else{ this.flag=true; this.shareTipFn(1,'分享失败!请稍后重试'); } }) .catch(error=>{ console.log(error); this.flag=true; this.shareTipFn(1,'分享失败!请稍后重试'); }); }, // 特殊字符转换 convertHtml:function(msg,flag) { msg = msg+""; //如果是数字,转为字符串 if (flag) { msg = msg.replace(/&/g,'&').replace(/\s/g,' ').replace(/</g,'<').replace(/>/g,'>'); } else { msg = msg.replace(/>/g,'>').replace(/</g,'<').replace(/ /g,' ').replace(/&/g,'&'); } return msg; },