拖延症害死锅

可编辑div模拟文本框的探索
发表于2017-10-24 16:22:14 | 分类:技术 | 阅读:86

如果一个论坛或社区的发帖和回复不能带图,那么其必定是索然无味的!


<textarea>,一个定义多行的文本输入框,但是其只允许插入文字,不能插入图片,并不能为所用,但 contenteditable 这个新属性解决了问题,它使元素的内容变成了可编辑。我们使一个<div>变成可编辑的,并获得内容值。


首先声明一点:因为之前已经实现使用添加过滤规则的标签<textarea>提交表单内容,所以决定仍通过其来实现对后台的传值,只不过多了一步:同步可编辑div的内容到<textarea>的文本区域内。其它方法没有详细了解,暂不说明。


1.<textarea>的内容除<img>标签外将代码都转化成html实体,防止其运行。


2.同步内容使用的是触发keyup事件。


测试1:输入单行文字。


在可编辑的<div>中输入一行纯文字“你好大锅[大锅博客]”。


1.png


测试2:换行


在可编辑的<div>中输入文字“第一行”再换行。


2.png


在可编辑的<div>中先换行输入文字“第二行”。


3.png


我的想法是:可编辑的<div>内,如果是直接输入文字时,文字就直接添加到这个可编辑的大<div>内,而如果换行输入时,则相当于往大<div>的内容结尾添加一个新的子<div>层,同时都可以编辑其内容。


在可编辑的<div>内,我们的视觉效果没有出错,但是我们最终要提交到后台的是<textarea>内的值,带标签的内容肯定不是我们想要的,在同步的时候需要加过滤规则。


测试的时候可以发现:输入文字时,<textarea>中<div>内的<br>标签被替换成文字内容,<br>不能作为一个换行的标识,我的方法是:使用<div>作为一次换行的标识,而<br>其实就是文本内容为空的状态,既然为空,不显示就行了,和闭合的</div>一样,替换为空,再传递给<textarea>就行了。


具体代码如下:


   text=text.replace(/<div>/g,"\r\n");(实现在<textarea>内的回车换行)
   text=text.replace(/</div>/g,"");
   text=text.replace(/<br>/g,"");


4.png


而像是第二种情况的换行再输入,会多产生一个子<div>,则会多一个换行,但我在提交表单内容时已经加了过滤规则,如果内容一开始为换行的话,则去除这些换行,保证第一行出现的必定不是空内容。


测试3:删掉回车 (这可能是我自己的问题)


在可编辑的<div>中,如果将光标放在第二行文字前进行一次或多次换行,再删掉换行,则在<textarea>中每行的文字会变成<span style="font-size: 1.3rem;">文字</span>


5.png


如果测试的是图片,则在<textarea>中图片会显示为:<img data-original="图片地址" style="font-size: 1.3rem;">,属性style="font-size: 1.3rem;"会使图片无法正常显示。


6.png


为了去除所有的html标签,可以使用


   replace( /<[^>]*>/g,"");  //找到所有的<>的标签,都替换为空格


但是为了阻止图片标签也被替换掉,可以先将<img>转成其它字符,待替换其它标签后再换回。然后图片中出现的属性样式,用的是比较暴力的方式,直接整句替换成空,记住空格都不能省略!具体如下:


   text=text.replace(/<img/g,"特殊名称");
   text=text.replace( /<[^>]*>/g,"");
   text=text.replace(/特殊名称/g,"<img");
   text=text.replace(/ style="font-size: 1.3rem;"/g,"");


7.png


测试4:在光标处添加图片 || 可编辑div中如何在光标位置添加内容


之前没有进行说明的图片都接在可编辑<div>的末尾,这对用户的体验很差,文字与图片的穿插很重要。找了个源码。上个作者也是找的,没有原址我就不生明,侵权删除。


完整代码如下:


<!DOCTYPE HTML>  
<html>  
<head>  
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>  
    <title>UMEDITOR 简单功能</title>  
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>  
    <script type="text/javascript" data-original="../third-party/jquery.min.js"></script>  
</head>  
<body>  
  
    <h1>UMEDITOR 简单功能</h1>  
    <script type="text/javascript">  
        $(function(){  
            $("#myEditor").focus(function(){  
                $("#myEditor").removeClass("flag");  
            });  
            /* $("#myEditor").blur(function(){  
                $("#myEditor").addClass("flag");  
             }); */  
        });  
      
        function add(){   
         insertHTML("<input type='text' disabled />");   
        }  
          
         //再加入一个全屏事件    
             $(window).click(function(e)    
                  {    
                    if (window.getSelection)    
                    {    
                         var getevent=e.data-originalElement?e.data-originalElement:e.target;//不要告诉我不知道这句的意思    
                      //console.log(getevent.id,getevent.tagName);  
                        if(getevent.id!=null && getevent.id=="cmdInsert"||getevent.id=="myEditor")    
                        {    
                            //alert(0);  
                            //代表 点了插入html的按钮    
                            //则不执行getFocus方法    
                         }    
                        else    
                            $("#myEditor").addClass("flag");//除非点了那个插入html的按钮 其他时候必须要执行getFocus来更新最后失去焦点的div    
                    }    
  
                  })   
          
                 
        function insertHTML(html)    
        {    
            var dthis=$("#myEditor")[0];//要插入内容的某个div,在标准浏览器中 无需这句话    
            //dthis.focus();  
             var sel, range;   
             console.log($(dthis).hasClass("flag"));  
             if($(dthis).hasClass("flag")){  
                 $(dthis).html(dthis.innerHTML+html);  
                 return;  
             }  
             if (window.getSelection)    
              {    
                     // IE9 and non-IE    
                     sel = window.getSelection();    
                     if (sel.getRangeAt && sel.rangeCount) {    
                     range = sel.getRangeAt(0);    
                     range.deleteContents();    
                     var el = document.createElement('div');    
                     el.innerHTML = html;    
                     var frag = document.createDocumentFragment(), node, lastNode;    
                     while ( (node = el.firstChild) )    
                      {    
                         lastNode = frag.appendChild(node);    
                      }    
  
                 range.insertNode(frag);    
                     if (lastNode) {    
                     range = range.cloneRange();    
                     range.setStartAfter(lastNode);    
                     range.collapse(true);    
                     sel.removeAllRanges();    
                     sel.addRange(range);    
                     }    
                    }    
             }     
             else if (document.selection && document.selection.type !='Control')     
             {            
                 $(dthis).focus(); //在非标准浏览器中 要先让你需要插入html的div 获得焦点    
            ierange= document.selection.createRange();//获取光标位置    
                 ierange.pasteHTML(html);    //在光标位置插入html 如果只是插入text 则就是fus.text="..."    
                 $(dthis).focus();        
  
             }    
        }     
    </script>  
    <button onclick="add()" id="cmdInsert" style="border: none;background-color: #fff;">增加节点</button>  
    <div id="myEditor" class="flag" style="position:absolute;width:800px;height:240px;border:1px;border-color: red;background-color: #f5f5f5" contenteditable="true">  
          
    </div>  
      
</body>  
</html>


提醒:在全屏事件中$(window).click(function(e){ });,最好console.log(e);然后根据点击的目标知道对象,像我之前错误认定点击目标,导致没有实现光标位置处准确添加内容。


补充:刚才说过在可编辑的大<div>内进行换行时,会产生多个子<div>,点击子<div>,我们知道其在大<div>中,但条件匹配不对,这会让光标位置对象发生误会,认为点击元素不在大<div>内,从而图片只上传到大<div>内容的末尾,而不是光标处。


我的方法是:(测试时候基本没出错,也有错误,自己调整。)

给每一个子<div>设置一个不一样的ID:
for(var i=0;i<$("#大div的ID div").length;i++){
     $($("#大div的ID div")[i]).attr('id','childDiv'+i);
}

判定条件由
 
if(getevent.id!=null && getevent.id=="上传按钮ID"||getevent.id=="大div的ID")

变成

if(getevent.id!=null && getevent.id=="上传按钮ID"||getevent.id=="大div的ID"||getevent.parentNode.id=="大div的ID")

多了一个父节点的判定

然后一开始的子<div>作为换行标识,也要修改成

for(var i=0;i<$("#大div的ID div").length;i++){
    text=text.replace(new RegExp('<div id="childDiv'+i+'">','g'),""); (实现在<textarea>内的回车换行)
}



小知识点:

js可以对变量进行全文替换之RegExp 对象

var key;   //key是变量
string.replace(key,"a");  //成功,只替换第一个
string.replace(/key/g,"a");   //失败,全文替换失败
string.replace(new RegExp(key,'g'),"a");    //成功


这就是我学习可编辑div模拟文本框遇到的一些问题,记录可能有点乱,额,就这样吧。


打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
点击此处,取消分享
*以下内容皆为必填项目

0条评论

暂无评论
TOP