刚开始我选择的是textarea标签,但是该标签不能随着文本内容自动变化高度,需要通过js操作dom来动态改变,比较麻烦,我就放弃了。选择了HTML5的一个属性contenteditable。
思路:默认时,输入框是有一个高度的。我这里的需求是输入三排文字后输入框高度就固定,所以你在算好相关padding,margin以及文字的line-height以后,给与输入框一个max-height即可。
<div id="textarea" ontenteditable="true" @focus="removeDefaultContent" @blur="addDefaultContent">
<img src="~images/index/[email protected]" alt="">
<span>随便说说</span>
</div>
思路:获取焦点的时候,通过js操作innerHTML把内容清空。失去焦点的时候判断innerHTML有没有值,没有的话,重新通过js操作innerHTML把span和img标签加上。
问题:通过js操作innerHTML把span和img标签加上,图片不显示,审查元素发现img标签的src地址是http://192.168.0.108:8089/images/index/[email protected],本来是本地的图片,现在反而是访问本地服务器了。
解决: img标签不写src属性,而是通过css样式添加背景图像。
结果:js操作innerHTML把span和img标签加上后,图片依然不显示,且我才发现img和span的css样式根本就没生效。
原因: vue中css编译完成后类名后会有随机码做唯一标识,这就导致了拼接的html中的类名与编译后的类名不同,也就无法生效了。
解决: 参考https://www.cnblogs.com/hhyf/p/11409386.html
我没有采用这种方法,想了下拼接的html不行的话,那我通过伪类元素直接给你自身添加,这样不管你的标识码怎么变,你始终还是那个你,给你加就没拐,哈哈哈。
思路: 通过伪类元素直接给输入框加默认的图片和placeholder内容,输入文字的样式通过id标签加上,伪类元素的样式通过class属性加上。当输入框获取焦点时,移除class样式,当失去焦点时,若没有输入内容或者全为空的情况下,添加class样式。
结果: 可。
html部分:
<!-- 回答问题部分 -->
<div class="to-answer-question">
<!-- 输入框部分 -->
<div ref="textarea" class="textarea" id="textarea" contenteditable="true" placeholder="随便说说" @focus="removeDefaultContent" @blur="addDefaultContent" @input="changeAnswerContent" @paste="optimizePasteEvent">
</div>
<!-- 发送输入内容 -->
<div id="send-answer" class="send-answer" @click.stop="sendAnswer">
<img src="~images/index/[email protected]" alt="">
<span>发送</span>
</div>
<!-- 分享功能(暂时不实现) -->
<div class="share-question" v-if="showShareQuestion">
<img src="~images/index/[email protected]" alt="">
<span>分享</span>
</div>
</div>
css部分(scss):
.to-answer-question {
width: 100%;
background: #fff;
padding: 8px 16px;
position: fixed;
left: 0;
bottom: 0;
z-index: 1000;
box-shadow: 0 -1px 1px 0 rgba(100, 100, 100, .05);
display: flex;
justify-content: space-between;
align-items: center;
#textarea {
width: 100%;
height: auto;
font-size: 13px;
color: #333;
line-height: 16px;
text-align: left;
margin-right: 20px;
background: #f8f9ff;
border-radius: 8px;
padding: 6px 13px;
max-height: 54px;
word-wrap: break-word;
overflow-x: hidden;
overflow-y: auto;
overflow-y: visible;
}
.textarea {
&:before {
content: '';
display: inline-block;
width: 16px;
height: 16px;
background-image: url('~images/index/[email protected]');
background-size: 100%;
vertical-align: middle;
margin-right: 8px;
}
&:after {
content: attr(placeholder);
display: inline-block;
color: #999;
font-size: 13px;
vertical-align: middle;
line-height: 16px;
}
}
.send-answer {
flex-shrink: 0;
display: flex;
justify-content: flex-start;
align-items: center;
img {
width: 16px;
height: 16px;
margin-right: 3px;
}
span {
font-size: 15px;
color: #ff586b;
line-height: 16px;
}
}
.share-question {
flex-shrink: 0;
margin-left: 12px;
display: flex;
justify-content: flex-start;
align-items: center;
img {
width: 20px;
height: 20px;
margin-right: 2px;
}
span {
font-size: 13px;
color: #333;
font-weight: bold;
line-height: 18px;
}
}
}
js代码部分:
export default {
data() {
return {
answerContent: sessionStorage.getItem('answerContent') ? sessionStorage.getItem('answerContent') : null, // 用户对于问题回答的内容
showShareQuestion: true, // 是否展示分享图标
}
},
methods: {
// 输入框失去焦点时触发
addDefaultContent(event) {
// 拿到当前内容
let textareaContent = event.target.innerText.trim()
// 没有输入内容或者输入了很多空格后失去焦点,则显示默认内容
if (textareaContent.length == 0) {
// 若全部输入的空格,则先清空
this.$refs.textarea.innerText = ''
// 给于伪类元素的css样式名
this.$refs.textarea.setAttribute('class','textarea')
// 展示分享按钮
this.showShareQuestion = true
} else {
// 仅用于用户在未登录时,在输入框中输入了内容的情况(登录后重新回到该界面时保留之前输入的内容)
if (!sessionStorage.getItem('userInfo')) {
sessionStorage.setItem('answerContent', this.answerContent)
}
}
},
// 输入框获取焦点时触发,去除默认placeholder且隐藏分享按钮
removeDefaultContent(event) {
if (event.target.innerText.length == 0) {
this.$refs.textarea.classList.remove("textarea")
this.showShareQuestion = false
}
},
}
}
过程: 在测试过程中,我发现每次截取前200个值重新赋值给输入框后,光标跑到了文首,这样就很不方便操作了,所以在重新赋值后需要处理光标,将光标放在文末。
结果: 可。
参考: https://blog.csdn.net/qq_31061615/article/details/80263746以及https://developer.mozilla.org/zh-CN/docs/Web/API/Selection
html输入框部分:
<div ref="textarea" class="textarea" id="textarea" contenteditable="true" placeholder="随便说说" @focus="removeDefaultContent" @blur="addDefaultContent" @input="changeAnswerContent" @paste="optimizePasteEvent">
</div>
js代码部分:
methods: {
// 监听输入框内容的实时变化,然后及时赋值给对应的变量
changeAnswerContent(event) {
let textareaContent = event.target.innerText.trim()
if (textareaContent.length > 200) {
this.$toast('最多允许输入200个字符哦')
textareaContent = textareaContent.slice(0, 201)
// 截取前200个赋值给当前文本框
this.$refs.textarea.innerText = textareaContent
// 将光标重新定位到内容最后
this.$nextTick(() => {
this.keepCursorEnd(event.target)
})
}
// 拿到用户输入的值
this.answerContent = textareaContent
},
// 将光标重新定位到内容最后
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()
}
},
}
过程: 在测试的过程中,发现2.3中光标的定位处理,会只拿当前内容的文本然后将光标定位到最后,这样其实也处理了复制内容插入的样式问题。我测了很多次都没问题,为了以防万一以及学习新知识,我还是加上了@paste="optimizePasteEvent"事件的监听来进行处理。
结果: 可。
参考: https://segmentfault.com/q/1010000009988202及https://developer.mozilla.org/zh-CN/docs/Web/API/ClipboardEvent/clipboardData以及https://developer.mozilla.org/zh-CN/docs/Web/API/Document/execCommand
html输入框部分:
<div ref="textarea" class="textarea" id="textarea" contenteditable="true" placeholder="随便说说" @focus="removeDefaultContent" @blur="addDefaultContent" @input="changeAnswerContent" @paste="optimizePasteEvent">
</div>
js代码部分:
method: {
// 监听粘贴内容到输入框事件,对内容进行处理
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)
}
},
}
问题: 在移动端测试的时候发现一个问题,输入框一旦获取焦点以后,点击其他任何地方(提交以及分享按钮除外)焦点会一直保留,这样就很恼火了。我收下输入框,由于焦点还存在,不管我点或者滑动其他地方时,输入框会马上弹上来。
解决: 监听当前页面的滑动事件,如果事件对象不是输入框时,则让其失焦。
注意: 离开该页面时,一定要移除事件监听,不然在某些地方点击或者触摸时可能会报blur of undefined错误。
结果: 可。
mounted() {
this.$nextTick(() => {
// 进入页面前看用户是否在登录前已经输入了文字,如果是的话需要展示之前的文字
if (sessionStorage.getItem('answerContent') != null) {
// 若有,则需要移除伪类默认样式
this.$refs.textarea.classList.remove("textarea")
// 展示用户之前输入内容
this.$refs.textarea.innerText = sessionStorage.getItem('answerContent')
// 隐藏分享按钮
this.showShareQuestion = false
}
})
// 监听点击事件对象是不是输入框,不是的话取消输入框焦点
window.addEventListener("touchstart", this.selectConfirmBlur)
},
// 离开组件前需要移除监听事件,不然会引起blur of undefined的报错
beforeDestroy() {
window.removeEventListener('touchstart', this.selectConfirmBlur);
},
methods: {
// 当触摸或点击位置不是输入框时,取消输入框焦点
selectConfirmBlur(event) {
if (event.target.id != 'textarea') {
this.$nextTick(() => {
this.$refs.textarea.blur()
})
// 如果是点击的发送,那么不仅取消焦点,还应该触发发送按钮
if (event.target.parentNode.id == 'send-answer') {
this.sendAnswer()
}
}
},
}
完。