另外一種場景下的js @提到好友 js實現@提到好友 關於qq空間評論回覆的一點研究 js實現@提到好友 js實現@提到好友

轉載請註明: TheViper http://www.cnblogs.com/TheViper html

在輸入框中@好友這個功能很常見,具體效果大概有兩種:node

1.像js實現@提到好友,在輸入框中輸入@時,根據@後面的字符,彈出相應好友菜單。chrome

2.增長一個按鈕,點擊後出現包含全部好友的彈出層。瀏覽器

本文就介紹本屌在實現第二種@時走過的一些坑。爲了簡單,其中的什麼彈出層,拉數據就不說了。app

先說下要求:編輯器

1.添加@好友時,不管編輯器有沒有焦點,@好友都會被添加到上一個焦點位置,效果和更簡單的 編輯器從光標處插入圖片(失去焦點後仍然能夠在原位置插入)中的同樣。函數

2.添加到上一個焦點位置後,不管編輯器有沒有焦點,光標自動出如今@好友以後。post

3.若是要刪除@,必須是@+好友一塊刪除,就像關於qq空間評論回覆的一點研究中刪除回覆好友同樣。圖爲qq空間評論框this

4.同2相似,刪除整塊@好友後,光標停留到@好友前面的位置。url

5.兼容firefox,chrome,ie6 7 8.

第一點和第二點用更簡單的 編輯器從光標處插入圖片(失去焦點後仍然能夠在原位置插入)就很容易實現,只不過這裏傳入的不是img src地址,而是html.至於傳入的html,仍是firefox傳入<img>標籤.

html="<img alt='@"+name+"' onclick='return false;' contenteditable='false' class='mention'>&nbsp;";

ie和chrome傳入<button>標籤。

html="<button onclick='return false;' class='mention' contentEditable='false' >@"+name+"</button>&nbsp;";

另外對於editor對象的插入方法須要像上一篇,作點修改,使得在原來光標位置插入html後,光標緊跟其後。

對於第三點,若是不寫其餘代碼看下效果有多坑爹。

firefox

能夠看到當光標移到@好友後面時,按backspace,怎麼都刪除不了。

chrome

能夠看到光標不光會跳到@好友裏面,甚至能夠直接鼠標點擊到@好友裏面。

ie8

能夠看到ie8竟然神奇的知足全部要求,ie6,ie7結果也同樣,就不貼出來了。

首先解決第三點,@好友必須是整塊刪除。

本屌想到的是對編輯器添加keydown事件,緣由參見js實現@提到好友。當按backspace時,判斷此時光標前的html的lastChild是否是相應的包裹@好友的標籤(firefox <img>,ie chrome <button>),若是是,就把標籤刪除了。具體的

獲取光標前的html,這個在js實現@提到好友中出現過,這裏由於後面要用到它的range,因此把range也返回了。

        function getTextBeforeCursor(containerEl) {
            var precedingChar = "", sel, range, precedingRange;
            if (window.getSelection) {
                sel = window.getSelection();
                if (sel.rangeCount > 0) {
                    range = sel.getRangeAt(0).cloneRange();
                    range.collapse(true);
                    range.setStart(containerEl, 0);
                    precedingChar = range.cloneContents();
                }
            } else if ( (sel = document.selection)) {
                range = sel.createRange();
                precedingRange = range.duplicate();
                precedingRange.moveToElementText(containerEl);
                precedingRange.setEndPoint("EndToStart", range);
                precedingChar = precedingRange.htmlText;
            }
            return [precedingChar,range];
        }

在現代瀏覽器中,這個函數返回的是FragmentDocument.而後把FragmentDocument中的lastChild,也就是相應的包裹@好友的標籤刪掉,再放回光標後的node前面。

            check_key:function(e){
                var htmlBeforeCursor=getTextBeforeCursor($('editor')),frag=htmlBeforeCursor[0],range=htmlBeforeCursor[1],
                last_node=frag.lastChild;
                if(last_node!=null&&last_node.nodeType==3&&last_node.nodeValue=="")
                    last_node=last_node.previousSibling;
                if(last_node!=null&&last_node.className=='mention'){
                    if(last_node.nodeName=='IMG'||last_node.nodeName=='BUTTON'){
                        frag.removeChild(last_node)
                        range.deleteContents();
                        $('editor').insertBefore(frag,$('editor').childNodes[0])
                        e.preventDefault();
                    }
                }
            }

第一個if是排除在刪除過程當中,可能會多出值爲""的TextNode.

固然也能夠經過其餘方法,找出相應的包裹@好友的標籤,而後removeChild()刪除。

效果

firefox

能夠看到相應的包裹@好友的標籤是能夠整塊刪除了,不過刪除後光標跑到最前面了。

chrome

和firefox同樣,能夠成功整塊刪除,可是這裏刪除後,編輯器沒有焦點了。

第三點貌似是解決了,下面解決第四點,刪除整塊後,光標跳到刪除塊以前。

注意到上一篇中document.execCommand("insertHtml",false,html);,能夠在插入後,讓光標緊隨其後。

        this.insertImage=function(html){
            this.restoreSelection();
            if(document.selection)
                currentRange.pasteHTML(html); 
            else{
                container.focus();
                document.execCommand("insertHtml",false,html);
                currentRange.collapse();
            }
            saveSelection();
        };

這裏對刪除完包裹標籤的FragmentDocument使用它,而不是以前的$('editor').insertBefore(frag,$('editor').childNodes[0]).具體的,

            check_key:function(e){
                var htmlBeforeCursor=getTextBeforeCursor($('editor')),frag=htmlBeforeCursor[0],range=htmlBeforeCursor[1],
                last_node=frag.lastChild;
                if(last_node!=null&&last_node.nodeType==3&&last_node.nodeValue=="")
                    last_node=last_node.previousSibling;
                if(last_node!=null&&last_node.className=='mention'){
                    if(last_node.nodeName=='IMG'||last_node.nodeName=='BUTTON'){
                        frag.removeChild(last_node)
                        range.deleteContents();
                        var div=document.createElement('div');
                        div.appendChild(frag);
                        document.execCommand("insertHtml",false,div.innerHTML);
                        div=null;
                        e.preventDefault();
                    }
                }
            }

建立一個div element,把FragmentDocument添加到裏面,而後經過div element的innerHTML獲得刪除完包裹標籤的html,最後document.execCommand("insertHtml",false,html);

效果

firefox

能夠看到firefox是達到要求了。

chrome

能夠看到刪除是成功了,光標位置也沒問題,就是多了不少無心義的空包裹標籤。這是由於chrome在document.execCommand("insertHtml",false,html);時,很坑爹的把<button>標籤的contenteditable屬性自動刪掉了,即使後面再去setAttribute添加contenteditable屬性也沒用。而在firefox下就不存在這個問題,<img>標籤仍然有contenteditable屬性,使得光標不會跳到標籤裏面。

怎麼解決?只有對chrome不用document.execCommand("insertHtml",false,html);.可是在光標處插入@好友的方法中也使用了document.execCommand("insertHtml",false,html);。沒辦法,只有重寫chrome在光標處插入@好友的部分,這裏要用到一直在監聽編輯器而隨時在變化的editor封裝裏的currentRange.

                if(isChrome){
                    html="<button onclick='return false;' class='mention' contentEditable='false' >@"+name+"</button>&nbsp;";
                    $('editor').focus();
                    var sel=window.getSelection(),range=at_editor.getRange(),node=avalon.parseHTML(html);
                    range.insertNode(node);
                    range.collapse();
                    sel.removeAllRanges();
                    sel.addRange(range);
                    // at_editor.insertImage(html);
                }

爲此還要在editor封裝裏暴露currentRange。

        this.getRange=function(){
            return currentRange;
        }

這下就能夠保證,在添加的@好友標籤中存在contenteditable=「false」了。另外,也不用在刪除時判斷FragmentDocument的lastChild是否是<button>標籤了。由於<button>標籤裏有contenteditable=「false」,刪除後光標會自動跳到@好友標籤前面位置。

效果

 

最後附上例子下載

更新:

firefox下,按<-左箭頭移動光標時,若是光標左邊是@好友標籤,就會刪除標籤,因此刪除標籤時須要判斷按鍵是否是backspace鍵。

                if(e.keyCode==8&&last_node!=null&&last_node.className=='mention'){
                  ....
                }
相關文章
相關標籤/搜索