我爱编程

仿微博发博的编辑框的实现

2018-04-17  本文已影响0人  阿聪太暴躁

前言:

前一阵做公司的项目,有一个需求需要实现类似微博发博的功能,包括话题、表情、图片的等功能的实现。第一反应是通过div的contenteditable属性实现,但在实践中踩到了不少的坑,而关于这方面的教程资料少之又少,磕磕绊绊的想了两天最后才实现。所以决定把自己的思路心得分享给大家,希望给同样需要这种功能的程序猿提供帮助。如果有大神指出我的问题,或者提供更好的方法我真的是十分感谢啦。



先上代码:仿微博发博

1.模拟placeholder属性

作为一个编辑框,需要给用户一个可输入的提示,也就是placeholder,但div contenteditable 的方法中并没有placeholder的属性,但通过css可以简单的模拟。

html代码:
    <div class="editArea" contenteditable='plaintext-only' data-text="请输入内容..." id="editArea"></div>
css代码:
.editArea:empty:before{
   content: attr(data-text);
   color: #999;
   font-size: 0.14rem;
   line-height: 200%;
   position: relative;
   top: -0.05rem;
 }

按理说以上代码可以实现类似与textarea的placeholder的效果,但在页面中发现当div中输入内容之后删除,会莫名多出来一个<br>标签,导致删除内容后虽然看起来是空的,但提示的文字不会出现。所以需要用js处理一下。
我的方法是监听backspace键,当按下的时候判断div中的内容并清空。

js代码
 $(".editArea").on("keyup", function(e) {
  if (e.keyCode == 8) { //监听backspace事件
    if ($(this).html().length == 0 || $(this).html() == "<br>") { 
      $(this).empty()
    }
}
}

2.图片上传功能

由于做的是移动端的页面,所以需要html通过<input type="file">直接唤起手机相册。
需要注意的是<input type="file">并没有办法变成各种花哨的样式,需要将其透明度设置为0,定位在图标或者按钮上。
<input type="file" accept="image/*" capture="camera">
<input type="file" accept="video/*" capture="camcorder">
<input type="file" accept="audio/*" capture="microphone">
accept 直接打开系统文件夹,image-相册、video-录像、audio-录音
capture可以捕获到系统默认的设备,camera--照相机;camcorder--摄像机;microphone--录音。


3.添加表情功能

表情功能的实现思路是通过按钮唤起自定义的表情面板,当点击表情的时候在编辑框中创建一个同样路径的<img> 元素。比较麻烦的是在表情面板中有一个删除按钮,需要js实现类似backspace的功能。

代码:
$(".backspace").on('click', function(e) {
  e.stopPropagation();
  e.preventDefault();
  // 判断是否为表情
  var str = $('#editArea').html()
  var patt = /<img[^>]*class="text_emoji_icon">/gi
  var arr = str.match(patt) ? str.match(patt) : []
  var char = arr ? arr.pop() : ''
  var lastIndex = str.lastIndexOf(char)
    // 判断是否为话题
  var patt1 = /<span class="topic">([^<]+)<\/span>&nbsp;/g;
  var arr1 = str.match(patt1) ? str.match(patt1) : []
  var char1 = arr1 ? arr1.pop() : ''
  console.log(char1)
  var lastIndex1 = str.lastIndexOf(char1)
  if (char) { //为表情则删除
    if (char.length + lastIndex == str.length) {
      var newContent = str.substr(0, lastIndex)
      $('#editArea').html(newContent)
    } else {
      var newContent = str.substr(0, str.length - 1)
      $('#editArea').html(newContent)
    }
  } else if (char1) { //为话题则整个删除
    if (char1.length + lastIndex1 == str.length) {
      var newContent = str.substr(0, lastIndex1)
      $('#editArea').html(newContent)
      set_focus()
    }
  } else { //为文本则删除一位
    var newContent = str.substr(0, str.length - 1)
    $('#editArea').html(newContent)
  }
})

em.....这里的代码写的比较杂乱。具体的思路依靠lastIndexOf来进行检测。
在点击backspace的按钮时,首先通过正则match()的方法检测文本中是否含有表情标签<img class='text_emoji_icon'>或者话题标签<span class="topic"></span>
match()方法本身会将匹配到的字段按先后顺序存为一个数组,所以我们取出数组最后的一个元素通过lastIndexOf()判断是否在文本的最后。
若元素在最后一位,则 lastIndexOf()的值+匹配文本的长度 = 整个文本的长度。至于删除就是对整个文本进行字符串的截取,就不说啦。

4.话题的添加

话题的主要需求有两个:

1.需要弹出一个推荐话题列表,当用户点击话题的时候在编辑框内添加对应的话题;

此功能的实现与表情添加功能如出一辙,不多赘述。

2.当用户输入双#号后,#与#之间的内容变成话题样式,甚至可以加上链接。

对于怎么判断双#之间的内容变成话题困扰了我好久,最后经过多次尝试,利用 keyup监听+正则匹配实现。
具体思路:用户按键时触发检查函数,如果检测到双#号,则将其与其内容放到标签中并替换文本。

代码:
    $(".editArea").on("keyup", function(e) {
     var str = $(".editArea").html()
      if (str.charAt(str.length - 1) == '#') { //监听用户输入#号事件
        if (countInstances(str, "#") % 2 == 0) { //判断#号数量来触发话题变色效果
          if (ReplaceTopic($(".editArea").html())) {
            var span = ReplaceTopic($(".editArea").html())
            $(this).html("")
            $(this).html(span)
            set_focus() //光标定位到最后
          }
        }
      }
    }

因为没必要每次触发事件都替换,所以我加了判断。

结语:

第一次写感觉写的有点混乱,文章中如果又出现错误或者需要改进的地方请大家不吝赐教。因为查了很多资料都没有查到比较官方的demo,所以自己绞尽脑汁的想出了这个办法。
文章的问题点是结合自己在做项目中的思路顺序下来,每一个部分都算是一个小知识点,这样为同样用到contenteditable的小伙伴们提供帮助。文章的末尾会放几个比较有用的函数供大家参考。

可编辑标签中光标始终定位在最后:
function set_focus() {
  el = document.getElementById('editArea');
  el.focus();
  if ($.support.msie) {
    var range = document.selection.createRange();
    this.last = range;
    range.moveToElementText(el);
    range.select();
    document.selection.empty(); //取消选中
  } else {
    var range = document.createRange();
    range.selectNodeContents(el);
    range.collapse(false);
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
  }
}
通过正则将文本内容修改
function ReplaceTopic(str) {
  var r, re; // 声明变量。
  var ss = str;
  var i = 0
  if (/\#([^\#|.]+)\#/g.test(ss)) {
    console.log(/<span class="topic">([^<]+)<\/span>&nbsp;/g.test(ss), "asss")
    if (/<span class="topic">([^<]+)<\/span>&nbsp;/g.test(ss)) {
      ss = ss.replace(/<span class="topic">([^<]+)<\/span>&nbsp;/g, function(
        word) {
        console.log(word, "----")
        return word.match(/\#([^\#|.]+)\#/g)
      })
    }
    r = ss.replace(/\#([^\#|.]+)\#/g, function(word) {
      console.log(word)
      return '<span class="topic">' + word + '</span>&nbsp;';
    });
    return (r); //返回替换后的字符串
  } else {
    return false
  }
}

我是一名用不停歇的小前端。
上一篇下一篇

猜你喜欢

热点阅读