最近有个自定义表情插件要放入移动端页面,折腾了几天,有了些成果,分享下。

关键两点点就是contenteditable元素光标设置,和保持该元素的输入状态。

先上几个API吧

getSelection ,他表示一个选中的文档片段,就是鼠标拖拽后蓝底白字的部分。包含了锚点Anchor,焦点Focus,范围range等信息

Range 是核心,range对象包含了片段的起点、终点、长度、节点等信息,也提供了操作改片段的几个方法。可以由getSelection()..getRangeAt(0)获得,也可以自己创建createRange()。创建的选区自然是为了添加表情到光标位置(重要)。

range.insertNode(node) 向range对象起点添加节点,添加文本节点用insertNodeContent()——这个两种节点都能添。

range.collapse(boolean) 折叠选区,由于range添加节点内容后是选中状态(蓝底白字),需要折叠range来恢复输入状态,接收一个布尔值,true则向起点折叠,false向终点。其实光标的闪烁就是range的起点终点保持重合了(比较重要)。

selection.removeAllRanges() 和 selection.addRange(range) 分别是移除选区所有range 和添加新的range。这两个方法是为了保持selection的更新,否则你用getRangeAt(0)获取到的range都是之前的(比较重要)。

接下来是事件函数封装,看完整组件代码(不熟悉Vue的,JS里的this可以当成window,看注释吧)

注意:不要使用fastclick,IOS上触发不了blur事件,BUG。可自行用touch事件代替click,如果有好用的类似插件,还请留下链接,谢谢。

<template><div :class="'footer ' + (!isWriting ? 'footer-hide' : '')" @click.self="footerBlur"><div class="main-position"><div class="main"><div :class="'left' + (isWriting ? ' writing' : '')"><!-- 编辑器元素 --><div class="content" ref="content"contenteditable="true"@blur="contentBlur"@click="contentFoucs"></div><div class="emoji" @click="showPackage"><i class="iconfont icon-laugh"></i></div><div class="send" @click="sendComment">发送</div></div><!-- 表情包 --><div class="emoji-package" v-show="show" @click="addImage"><div class="page1"><img :src="'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/' + n + '.gif'"alt="" v-for="n in 21" :key="n" /></div><div class="page2"><img :src="'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/' + (n + 21) + '.gif'"alt="" v-for="n in 21" :key="n" /></div></div><div class="center" @click="startWrite" v-show="!isWriting"><p v-if="content"><span class="sketch">[草贴]</span><span v-html="content"></span></p><p v-else>写评论</p></div><div class="right" v-show="!isWriting"><div class="share" @click="doShare"><i class="iconfont icon-share"></i></div></div></div></div></div>
</template><script>
export default {name: 'footerbar',data () {return {content: '',preContent: '写评论',show: false,range: {},selection: {},isWriting: false,items: 40}},methods: {contentBlur () {/*** 每次焦点移出都要保存一次当前 range ,后面好放回来* 由于输入框以外的点击都会使输入框失去焦点,不知道会有什么操作,故勤保存*/this.range = this.selection.getRangeAt(0)},startWrite () {this.isWriting = trueif (this.content) {// 如果focus前有内容, 光标设置到之前保存的位置 setCursorthis.setCursor()} else {// 开始评论,输入框自动聚焦this.$refs.content.focus()this.contentFoucs()}},contentFoucs (e) {/*** range更新到点击的位置* 点到图片就根据点击位置和图片大小设置一个合理的位置(前或后)*/let node = e ? e.target : {}this.range = this.selection.getRangeAt(0)if (node.tagName === 'IMG') {this.setCursor(node, e.offsetX < node.width / 2)}},showPackage () {this.show = !this.showthis.setCursor()},addImage (e) {/** * 点击emoji图片后,复制节点添到当前range里并设置光标和更新range * 若是点到其他地方则设置到之前的位置*/if (e.target.tagName === 'IMG') {let node = e.target.cloneNode(true)this.range.insertNode(node)this.setCursor(node, false)} else {this.setCursor()}},setCursor (node, before) {/*** node 为传入的节点,不传则foucs到之前保存的位置* before 控制折叠方向*/if (node) {// 需要新建一个range来添加内容let range = document.createRange()range.selectNode(node)range.collapse(!!before)this.selection.removeAllRanges()this.selection.addRange(range)// 更新 rangethis.range = range} else {this.selection.removeAllRanges()// 使用之前的this.selection.addRange(this.range)}},footerBlur () {// 退出输入状态,保存内容this.show = falsethis.isWriting = falsethis.content = this.$refs.content.innerHTML},sendComment () {this.content = this.$refs.content.innerHTMLif (!this.content) { return }this.$emit('comment', this.content)this.show = falsethis.isWriting = falsethis.content = ''this.$refs.content.innerHTML = ''},doCollect () {},doShare () {}},mounted () {// 先得到selection,并创建一个rangethis.selection = document.getSelection()this.range = document.createRange()}
}
</script><style lang="stylus" scoped>
div.footer-hidetop auto
.footerposition fixedtop 0// top calc(100% - 1rem)bottom 0left 0width 100%.main-positionheight .8remline-height .8remposition absolutebottom 0left 0width 100%.maindisplay flexborder-top 1px solid #eee// height 100%position absolutewidth 100%bottom 0background #fffalign-items flex-end.emoji-packageborder-top 1px solid #dddposition absoluteright 0bottom 100%background #ffffffwidth 100vwheight 2.4removerflow-x scrolluser-select none>divheight 100%display gridgrid-template-columns 1fr 1fr 1fr 1fr 1fr 1fr 1frjustify-items centeralign-items centerdiv.writingwidth 100%max-height none.leftdisplay flexwidth 0max-height .8rembackground #fafafaoverflow hiddenposition relativealign-items flex-end.emojifloat rightwidth 1remtext-align centerline-height 30px.iconfontfont-size .5rem.sendwidth 1remtext-align centerpadding-right .2remfont-size .32remcolor #666.contentflex 1align-self stretchbackground #fffwidth 100%min-height 100%max-height 5emoverflow-x hiddenoverflow-y scrolloutline nonepadding .1rem .2rembox-sizing border-boxline-height 1.4border 1px solid #dddborder-radius 3pxword-break break-all>imgwidth 1.4em.centerheight 100%padding .1rem .2rem .1rem .3remflex 1overflow hiddenbox-sizing border-box.sketchcolor #f33pbackground #eeecolor #999height 100%border-radius .7remline-height .6rempadding 0 .2removerflow hiddentext-overflow ellipsiswhite-space nowrap.rightwidth 1.8remdisplay flexheight 100%position relativetext-align center>divwidth .8rem.iconfontfont-size .4rem
</style>

contenteditable元素的光标控制,自制emoji插件相关推荐

  1. emoji 乱码_这个自制emoji的网站,让你成为永远不输的斗图王者

    作为表情界的元老级人物,不管是苹果官网输入法.微信官方表情还是各个主流输入法里,我们都可以从里面找到大量 emoji 表情. 然鹅--就算这么多表情,小帮每次发 emoji 时还有有些选择困难. 因为 ...

  2. HTML5 progress元素的样式控制、兼容与实例

    一.progress元素基本了解 基本UI progress元素属于HTML5家族,指进度条.IE10+以及其他靠谱浏览器都支持.如下简单code: <progress>o(︶︿︶)o&l ...

  3. Vue v-for 时,单个元素class的控制

    Vue v-for 时,单个元素class的控制 只要一句表达式,加一个控制控制变量 <template><ul><li v-for="(item,index) ...

  4. android设置webview光标颜色,CSS自定义设置元素闪烁光标颜色

    这次给大家带来CSS自定义设置元素闪烁光标颜色,CSS自定义设置元素闪烁光标颜色的注意事项有哪些,下面就是实战案例,一起来看一下. 前言因为业务需求, 要求我们的input框内的文本与悬浮的光标颜色不 ...

  5. 混合特征目标选择用于基于BCI的二维光标控制

    原文:Long J, Li Y, Yu T, et al. Target selection with hybrid feature for BCI-based 2-D cursor controlJ ...

  6. html标签设置提示语,contenteditable元素的placeholder输入提示语设置方法

    在某些情况下,textarea是不够用的,我们还需要显示一些图标或者高亮元素,这就需要用富文本编辑器,而富文本编辑器本质上是HTML元素设置了contenteditable. 然后可能需要像input ...

  7. 为 contenteditable 元素添加占位符

    假设我们想要为具有给定 contenteditable 属性的元素设置一个占位符: <div contenteditable></div> 注意:contenteditable ...

  8. contenteditable 元素的placeholder

    假设我们希望给定contenteditable元素有一个占位符: <div contenteditable></div> 1.使用:empty选择器 我们使用自定义属性 ,da ...

  9. contentEditable 中光标控制

    文章目录 简易 div 输入框 1.移动光标到开始或结尾 2.在光标处插入内容 简易 div 输入框 HTML5规范引入了 contenteditable 属性,它几乎可以用在任何元素上,只要添加这一 ...

最新文章

  1. Sublime插件支持Sass编译和Babel解析ES6 .sublime-build文件初探
  2. 6.OD-Run trace /Hit trace
  3. CNN基本步骤以及经典卷积(LeNet、AlexNet、VGGNet、InceptionNet 和 ResNet)网络讲解以及tensorflow代码实现
  4. Mysql1 晨考题
  5. IDEA 设置护眼色RGB值
  6. the android sdk location cannot be at the filesystem root
  7. 谐波合成法matlab,基于Kaimal谱采用谐波合成法生成脉动风场
  8. S7503E V7 snmpv3典型组网配置案例(与IMC联动)
  9. hive對於數據是懶加載的_05-配置数据库的远程连接 创建hive数据库的时候要选择latin1...
  10. Qt Pro语法总结
  11. 为多个VLAN实现DCHP
  12. Scikit-Learn (浅谈Kmeans聚类算法)
  13. wps linux 字体目录在哪个文件夹,WPS OFFICE怎么添加字体?(我下载的字体文件应当放那个文件夹?)...
  14. C# - Poker Sort
  15. floyd与传递闭包
  16. 完美解码播放器(PotPlayer)关闭自动播放下一首的方法(找了好久)
  17. OpenCV教程(5)函数整理
  18. 天猫商城在线购物系统
  19. 用计算机技术辅助语文教学,计算机技术相关论文范文文献,与计算机辅助语文教学相关毕业论文模板...
  20. 随时随地掌上邮,飞邮Android版邮件客户端正式提供试用

热门文章

  1. VMware---之网卡设置
  2. js的promise用法
  3. selenium 自动化测试面试题及答案
  4. 5978 Problem F 【递归入门】走迷宫
  5. Python —— Numpy详细教程
  6. SpringMVC的核心思想,概念及特点
  7. python如何计算圆周率到千万位
  8. 4年级计算机 设计贺卡教案,川教版四年级下册信息技术教案 设计贺卡.doc
  9. 免费领取kindle paperwhite6,码农键盘,人体工学鼠标~
  10. 利用Postgresql+Postgis进行空间地理信息分析(道路偏移,进出电子围栏等)