动手实现并开源IDEOnline——代码高亮【富文本编辑框】

来源:互联网 发布:mysql安装教程5.7.20 编辑:程序博客网 时间:2024/05/19 04:51

百度了很久,貌似类似的开源代码还没有人贡献(翻墙的不算),鄙人不才,尝试用了近期学的前端知识,做了这样一个简陋的代码编辑器,一是对之前所学知识的总结,二是抛砖引玉,期待能和大佬交流学习。

首先吹一下效果:

图.....没刷出来

以上就是实际运行效果,根据输入的字段进行匹配,并将匹配成功的部分高亮显示(也就是关键字的高亮);对于数字,支持整数、浮点数、科学计数法,如图显示为原谅色(拿到源码的可以自行在css中进行修改)。支持WebKit内核的浏览器,支持高版本IE(测试使用的是IE11)。

另外,文档内容极简,如图:

图.........也没刷出来

常驻文档的只有一个可编辑div,不像别的在线ide那样,一个textarea一个div改z-index覆盖来覆盖去的……这里只有一个div,即使用到textarea,也是动态生成的,并且用完就被丢弃。(也正因此涉及到光标跑来跑去的,到处定位。这几天做的头皮发麻……不过好歹还是完成了。)


然后说一下缺陷:

1.对于匹配成功一次的字段,一旦设置完成了样式,不管对其内容进行何种修改,样式不会还原。比如:输入var ,变红,然后将它改成vasari,他还是红色的。

这个可以通过RegExp来debug掉,目前只会一点皮毛,先行放置,留待后续学习。

2.一个文字节点之后跟随的标点符号,会染上该节点的颜色,直到下一个文字节点为止。

这个bug属于我对键盘敲击事件的event.which分的不够细导致的,标点符号分起来太多了,我只是简单的写一下,理一理思路。

3.ctrl+V的文本有显示不全的问题,不过只是看起来不完全而已。这个出现的原因后面再说,免得各位看官不懂我在胡言乱语些什么。

4.欢迎各位大佬捉虫。

 

下面正式进入代码分析部分

涉及到:少量css知识,少量HTML知识,JavaScript,jQuery。

可以收获:富文本编辑框的实现方式,富文本编辑框插入图片、链接的方法。

 

首先谈谈思路:

完整的一次编辑过程,从div获得焦点开始,到div写入带样式的文字结束。

div获得焦点

保存焦点位置

div开始编辑内容

       输入文字(字母+数字)

              生成textarea,焦点交给textarea

       输入文字以外的内容

              取出保存的div的位置,

插入textarea的文字和样式,

焦点挪到插入文字的后面,

删掉textarea。

div编辑完成。

这样就可以保证常驻文档的只有一个div,不存在任何被隐藏或遮盖的冗余文字。

 

上面的思路换算成文档事件的话也就是:

1.div的onkeydown事件,保存光标位置,在光标所在位置生成textarea,交付输入焦点;

       保存光标位置:

首先在函数外创建一个变量rangeClone,用来保存这里的光标位置。

if(document.selection){

          varsel = document.selection.createRange();

       rangeClone=sel;

//IE

       if(window.getSelection){

          varsel = window.getSelection();

           rangeClone=sel.getRangeAt(0);

//Chrome

       然后要在光标位置生成textarea,则要获取到光标在文档的坐标位置代码如下:(分别接在两个if后面)

       //IE

       if (sel.type !="Control") {

          range= sel.createRange();

          range.collapse(true);

           x=range.boundingLeft;

           y=range.boundingTop;

    }

}

       //Chrome

              if (sel.rangeCount){

              varrange = sel.getRangeAt(0).cloneRange();

              if(range.getClientRects){

              range.collapse(true);

              varrects = range.getClientRects();

              if(rects.length>0) {

                  varrect = rects[0];

                }

               //光标在行首时,rectundefined

                if(rect){

                  if(rect.left){

                       x=rect.left;

                   }

                  else{

                       x=8;

                   }

                   y=rect.top;

                }

       }

      //回退并添加一个空节点

       if((x == 0 && y==0)||rect === undefined) {

               varspan = document.createElement("span");

               if(span.getClientRects){

               //span添加一个宽度为0的空字符,确保span拥有内容和位置

                                  span.appendChild(document.createTextNode("\u200b") );

                   range.insertNode(span);

                   rect= span.getClientRects()[0];

                    x=rect.left;

                    y=rect.top;

                   varspanParent=span.parentNode;

                   spanParent.removeChild(span);

                   spanParent.normalize();

               }

        }

     }

}

这里有个很关键的点,我花了半个下午事件debug终于找出原因,写在注释部分了。也就是当输入了enter后,光标到首行,此时由于是空的节点,所以长度为零,rect也就未定义了。所以要在后面进行处理,即添加一个没有宽度的占位字符,辅助定位。

       根据这里取到的x和y,就能确定即将生成的textarea应该定位在何处了,

if((event.which>=96&&event.which<=105)||(event.which>=48&&event.which<=57)||(event.which>=65&&event.which<=90)||(event.which>=97&&event.which<=122)){

//当输入字母,数字。

vartxt="<textareaclass='showText' style='position: absolute; top:"+y+"px;left:"+x+"px'></textarea>"

           $("#showDiv").after(txt)

           valClone=$("#showDiv").html();

          varspanSeat= document.createElement('span');

           spanSeat.innerHTML="&nbsp&nbsp&nbsp";

           spanSeat.setAttribute("id","spanSeat");

           rangeClone.insertNode(spanSeat);

           $(".showText").focus();

       }

       可以看到这里并不是简单的定义好x,y之后就结束了,后面那部分代码的作用是,在div的相同的位置生成一个span,作为占位符,避免在插入文字时,textarea的空间对div后面的内容遮盖(有兴趣可以尝试屏蔽一下看看效果。)

 

2.textarea的onkeydown事件,接收到字母数字时,保留字母数字,并扩展textarea的宽度(防止遮盖。这就是导致粘贴的文本显示不全的原因,textarea宽度是根据按了多少个键来增减的,ctrlA只有两个键,却增加了大量文本内容,自然就出现了显示不全的毛病。)代码分析如下:

if((event.which>=96&&event.which<=105)||(event.which>=48&&event.which<=57)||(event.which>=65&&event.which<=90)||(event.which>=97&&event.which<=122)){

       //输入了文字,改变了文本框内容

        var widthOld =parseInt($(".showText").css("width"));

        $(".showText").css("width",widthOld+14+"px");

    }

   else if(event.which==8||event.which==46){

       //按了Backspace或者delete

      varwidthOld= parseInt($(".showText").css("width"));

       $(".showText").css("width",widthOld-14+"px");

      if($(".showText").val().length==1){

           $(".showText").val("");

           $(".showText").blur();

          if(window.getSelection){

              rangeClone=window.getSelection().getRangeAt(0);

              rangeClone.selectNodeContents(spanOld);

              rangeClone.setStart(spanOld,spanOld.length);

              rangeClone.collapse(false);

             window.getSelection().removeAllRanges();

             window.getSelection().addRange(rangeClone);

           }

       }

    }

   else{

       $(".showText").blur();

       $("#showDiv").focus();

      if(window.getSelection){

           rangeClone=window.getSelection().getRangeAt(0);

           rangeClone.selectNodeContents(spanOld);

           rangeClone.setStart(spanOld,spanOld.length);

           rangeClone.collapse(false);

          window.getSelection().removeAllRanges();

          window.getSelection().addRange(rangeClone);

       }

    }

简单来说就是分析按键的Unicode值,当输入字母数字时,扩展textarea的长度,

按下退格或者删除时,减少textarea的长度,并在其长度减少为0时,删去textarea节点。

而输入符号则代表需要关键字匹配的文字部分已经输入完毕了,此时把焦点交还给div,并把光标设置到div新增内容(保存在一个span节点中)之后,这样就完成了。

这里是通过range来实现光标位置设置的,首先将range锁定到刚添加的span上,此时相当于有一个蓝块包裹着span:span,就像这样。这个蓝块就是range,也就是我们平常鼠标拖动选择字段时的那个蓝块。然后把这个蓝块块的起点往后移动span的长度,光标也正是在这个起点处,因此光标就顺利的移动到了刚添加的span节点的末尾。接下来,只需要清除因为自动focus而生成的位于div头部的光标,然后在添加我们手动生成的这个光标,就达成了光标移动到刚添加的文字后面的效果。

 

3.textarea的onblur事件,主要做收尾工作,即复制和添加textarea的文字,样式,并保存到div,删除上面设置的占位节点,以及textarea。

$("body").on("blur",".showText",function(){

    $("#showDiv").focus();

   varspan = document.createElement('span');

    span.setAttribute("class",regExp($(".showText").val()));

    span.innerHTML=$(".showText").val();

    rangeClone.insertNode(span);

    spanOld=span;

    $("#spanSeat").remove();

    $(".showText").remove();

})

 

4.补充事件:textarea的onkeyup事件,因为在keyup的时候,textarea.val()的值才会更新,所以,需要在这里对占位符的内容进行设置,才能保证占位符的大小与textarea完全一致,既不出现遮盖,也不会断层,整齐美观。

$("body").on("keyup",".showText",function(){

document.getElementById("spanSeat").innerHTML=$(".showText").val();

})

对于占位符的文字,如何避免重影呢?css设置字体颜色为透明即可。

color:transparent;

 

css部分由于没有做太多设置,仅仅是对div盒子的几个参数设置了一下,在此就不赘述了。

以上,在线IDE的代码高亮部分就设置完成了,后面几天会研究下自动缩进,再后面会尝试用Ajax做代码补全。

原创粉丝点击