editer技术日志

要实现一个类似QQ说说的“@朋友”功能(输入框中输入@可以选择朋友),只是比功能QQ还要稍多的是还要自动发邮件。类似QQ的,表情,hashtag(输入#话题)系统全部具备。

首先,我可以选用textarea控件用作输入框,也可以使用DIV(js加载将DIV的contentEditable 设为 true变为可编辑模式)

我们来比较一下这两者之间的区别

1:使用textarea兼容性不好,主要体现在在非IE浏览器中很难做到即时预览,比如在chrome中无法看到插入的表情图片,而DIV可以轻易做到,只是在获取值的稍有区别,在textarea中获取整个HTML值最好使用jquery的$(text).HTML(),获取纯字符串使用$(text).val(),但是在非IE浏览器中$(text).HTML()似乎有些差别,$(text).val()获取的值是一样的,但是是纯字符。但是好处是可以使用服务器控件构造textarea(带TextMode="MultiLine"的TexBox)获取值,而使用DIV的话仿佛就不是很容易,可能只能通过JS来获取,若一定要与服务器交互,比较好的方式是复制到一个服务器隐藏域之中,但是必须得注意asp.net自动页面缓存,我喜欢使用javascript、ajax异步,所以我觉得使用DIV的话相对就要简单方便一点。顺便多嘴一句:DIV使用innerHTML获取带html标签的字符串,使用innerText获取纯字符,注意$(DIV).innerHTML不被jquery支持,可以写成$(DIV).get(0).innerHTML转换成html对象再操作,或者还是使用传统document.getElementById吧。

2:使用textarea的话,支持textareaId.createTextRange()获取选区对象;而可编辑DIV只能使用document.selection.createRange()获取。

3:在textarea之中插入带特殊字符串(例如标签符号'<'、'>')会自动编码,所以造成控件元素不能在其中直接显示(显示为<>,但是实际经过的编码,不能正常显示标签的对象),解决方法为使用jquery的append方法,但是使用append方法只能插在textarea在最后面,不能做到光标处插入。那怎么办?OK,综合一下:

使用方法:

function zyChart(str) {

    str = str.replace(/&lt;/g, "<");

    str = str.replace(/&gt;/g, ">");

    return str;

}

实现转义,然后使用

var C = document.selection.createRange();

    if (C) {

        C.text = html_val;//在IE里面,textarea不支持pasteHTML,只能使用C.text = val,这个对插入标签有很大的局限性

        $(B).html(zyChart(B.innerHTML));

    }

实现在光标处插入,这个可以实现在光标处插入图像,而且由于使用了$(B).html(zyChart(B.innerHTML))重新替换了内容,所以光标会失去插入元素之前的光标位置。在连续插入表情的时候很麻烦,需要不断重新选择光标。

OK,那使用DIV,很顺利,使用方法:

function insertContent(value) {

    //可以用$.browser.msie这是Jquery用来检测浏览器是否为IE

    Editer.focus();

    if ($.browser.msie) {

        if (document.selection) {

            Editer.focus();

            var C = document.selection.createRange();

            if (C) {

                C.pasteHTML(value);//DIV支持pasteHTML,这个是我选用DIV的重要原因

            }

        }

    } else {

        document.execCommand('InsertHtml', false, value);//chrome暂不支持execCommand,firefox没问题

    }

}

可以实现随意插入标签对象,而且不会被编码,即时预览也就很好,而且不会失去当前光标位置,预览性和方便性俱佳,但是DIV有个无法回避的问题,就是当插入@时,如果@两边都有文字的时候会自动识别为邮件格式(还能识别URL格式),然后变成<a href='mailto:***'/>,这个令人头疼的特性虽然只有IE具备,但是谁叫选择首先要兼容的就是IE呢。

以上三点为textarea和可编辑div的主要区别,接下来继续思路

我选用了可编辑div,那么实现插入表情和hashtag的功能就太容易了,只是插入hashtag后需要自动选中‘#’和‘(空格)’之间的文字,这个我可以使用以下处理方式:

    var text = "#Insert hash tag here "

    insertContent(text);

    moveAtCaret(21);//光标往左移动21个字符,因为光标之前停在最后一个hashtag字符

    Editer.focus();

    var rng = document.selection.createRange();

    rng.moveEnd("character", 20);

    rng.select();//往右选中20个字符,即选中了‘Insert hash tag here’字符

OK,hashtag基本搞定,选中进入最复杂的@邮件选人功能,以下是主要遇到的问题

问题1:如果在文本字符之间输入@,DIV立即识别为邮件超链接

问题2:输入@后弹出选人层,怎么得到搜索字符串,如果在@后面输入搜索朋友的字符(类似百度自动完成功能),怎么精确获取搜索字符串是个问题,选中对象后还必须清空原编辑区域内的搜索字符串,这个是一个挑战,后来想到一个比较好的方式,是输入@后在弹出层里面新加入一个input的text控件输入搜索字符串,这个就可以很容易获取搜索字符串,可是又产生了两个新的问题,一是弹出新层过后,焦点自动移动到了搜索框之中,可编辑DIV随即失去了焦点位置信息,那么如何将选定的对象放在失去焦点前的位置呢?第二个问题也比较烦人,在用于搜索字符的input控件中输入后回车会出现页面回发导致刷新页面,本来使用onkeydown="if (event.keyCode == 13){ event.returnValue = false}可以很容易的防治回发,但是悲剧的是唯有IE9完全不给面子,照样回发不误。

问题3:方向键可以选择人员,这个可以通过监听按键事件,我利用的判断选定层(动态搜索出的人用div包裹)的背景颜色进行判断,问题是明明赋值的颜色信息为#b2ddf6,可是获取的是rgb(178, 221, 246)"格式,可是之前的一个demo是没问题的,至少IE9、chrome是这样的,这个问题实在搞不懂,网上资料也少,搜了几篇英文文档,不知所云。

下面分别是解决方案:

1:禁止在可编辑DIV之中输入@,通过判断输入@的组合键(shift+2),if (event.shiftKey && event.keyCode == 50),然后return false阻止掉,在return false前打开搜索层,然后在选定过后往编辑区域插入的<span>标签中第一个字符串加入@,这样就绕过的浏览器的自动判断邮件功能。

2:由于focus到搜索框,可编辑DIV失去了焦点,思路就是要在搜索层弹出之前保存光标信息,以下方法能够获取光标信息,实现同样功能的方法很多,我随便选择了一个:

function savePos() {

    textBox = document.getElementById("edit_div");

    var start = 0;

    var end = 0;

    //Firefox

    if (typeof (textBox.selectionStart) == "number") {

        start = textBox.selectionStart;

        end = textBox.selectionEnd;

    }

    //IE6

    else if (document.selection) {

        var range = document.selection.createRange();

        if (range.parentElement().id == textBox.id) {

            var range_all = document.body.createTextRange();

            range_all.moveToElementText(textBox);

            for (start = 0; range_all.compareEndPoints("StartToStart", range) < 0; start++)

                range_all.moveStart('character', 1);

            // 计算一下\n

            for (var i = 0; i <= start; i++) {

                if (textBox.innerHTML.charAt(i) == '\n')

                    start++;

            }

            var range_all = document.body.createTextRange();

            range_all.moveToElementText(textBox);

            for (end = 0; range_all.compareEndPoints('StartToEnd', range) < 0; end++)

                range_all.moveStart('character', 1);

            for (var i = 0; i <= end; i++) {

                if (textBox.innerHTML.charAt(i) == '\n')

                    end++;

            }

        }

    }

    gbstar = start;

    gbend = end;//gbstar、gbend是全局变量

}

注意,此方法获取的光标位置不是以编辑框内的HTML字符为基础的,是纯字符串为基础,例如,编辑域内html字符串为‘<span>123456<span>789’,光标停在‘7’之后,获取到的光标位置为7,而不会算上<span>所占用的位置,我找了很多资料,想得到能不能有可以算上标签字符串的,但是很遗憾,流汗...

然后来说说回发:input的text控件在IE9之中要回发刷新页面,在对微软再次表示失望之后思考了一番,想出有两种方式可以解决,一个是在keydown事件中阻止掉回车,一个是使用textarea,textarea回车不会回发,但是注意会换行,第一种我没测试,我选择了第二种,使用textarea,至少安全。

然后第三个问题,还有很多方法可以实现方向键选择,但是确实不想换方法了,觉得这种方便简单,多加一个判断即可

$("#people_div div").each(function () {

        i++;

        if ($(this).css('background') == "#b2ddf6" || $(this).css('background') == "rgb(178, 221, 246)") {

            chooseIndex = i;//chooseIndex为选中DIV的序列号,之后可以使用$("#people_div div:nth(“+(chooseIndex-1)+”)")获取

        }

    });

然后,我们将插入的<span>标签和img表情图片变为不可编辑模式(图片表现为可以拖动图片大小),img表情很容易,直接在标签属性里面加入unselectable='on'不可选中即可

但是带文字的span还需使用js补上一个特型,和可编辑DIV使用的值相反,将contentEditable 设为 false就可以了,可以实现整体删除,不可选中和不可编辑内部文字。

最后是使用$(div).find('span')就可找出所有的span标签,获取已经选中的人,到这里js部分基本完成,一起玩转web编辑器吧,空了想做个简单的编辑器来玩,但是面临最大的问题仍然是浏览器的兼容问题!头疼啊.............

posted @ 2011-10-11 13:52  小伟风中转  Views(291)  Comments(0Edit  收藏  举报