美文网首页我爱编程
仿微博发博的编辑框的实现

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

作者: 阿聪太暴躁 | 来源:发表于2018-04-17 09:27 被阅读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
      }
    }
    

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

    相关文章

      网友评论

        本文标题:仿微博发博的编辑框的实现

        本文链接:https://www.haomeiwen.com/subject/hpxhkftx.html