最近在網上碰到一個人問了我一個問題,在可編輯div中插入文字或者圖片。因為web線上編輯器我從來只是用,基本不會去研究原始碼。後來正好一個線上聊天web項目中也要用到這個功能,我就特地看看了代碼。
基本上編輯器或者線上聊天web頁面,是不太可能用textarea在做輸入框的,因為我們可能要插入圖片或者超級連結,因此選擇在iframe或者div作為輸入框是必須的。
我這裡用的是 div.
要使div可編輯 必須 加入 contentEditable="true" 這個屬性。
然後就是擷取游標位置(或者選擇文字位置)進行文字或者html的插入 。
由於Firefox等標準瀏覽器支援getSelection方法,IE9以上也支援,但是萬惡的iE6-8不支援,因此要分兩部分代碼來寫。由於這些代碼很簡單,以下先貼一遍
function insertHTML(html) { var dthis=$("#div3")[0];//要插入內容的某個div,在標準瀏覽器中 無需這句話 var sel, range; 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(); } }
以上代碼基本 完成了 在可編輯div中 插入指定的html內容,這些代碼在baidu或者google中到處可以搜到,因此不再解釋為什麼這麼寫(太普遍了)
執行後 會發現在IE或者非標準瀏覽器中 是正常的。在Firefox或者chrome中 就不正常了
譬如 以下頁面 ,我有 不定數量的div(可能是程式動態產生),我只需要其中某一個div進行html的插入,其他不需要。
.....其他html元素.....<div id="div1" contentEditable="true" ></div><div id="div2" contentEditable="true" ></div> <div contentEditable="true" id="div3"></div> <input type="button" id="cmdInsert" onclick="執行向div3插入html方法"/>.....其他html元素.....
如上頁面 我只需要div3 支援插入html 其他兩個 只是可編輯而已。
使用上述代碼會發現,如果最後一個失去焦點的是 div3 那麼一切正常 如果 不是div3 或者 我又點到頁面其他控制項或者空白處,會發現插入的html沒有插入到我們想要的div3中而是插入到了 其他地方。
這其實不是bug,而是正常現象,getSelection 可以橫跨很多域,因此無法保證 獲得出來的range一定是你需要的div
這裡我再次申明,我實在不想看(哪怕看一眼)國內的線上web編輯器是如何?的。經過我翻查了度娘和google發現有個思路可以解決。
其實我們要解決的就是一件事情,每當頁面上的元素(包括div或者任意元素) 獲得焦點又失去後,我們只需獲得最後一個失去焦點的div是否是div3,如果是則執行上述代碼,如果不是直接在div3的內容後面加入要插入的html(寫入程式碼就可以。不要告訴我 不會)
一開始我想到的辦法是對div3設定一個click事件以及focus事件,當滑鼠點進去或者獲得焦點時 把一個變數 譬如叫做isdiv3 設定為true,點其他地方設定為false(這個方法實際上是行不通的,這裡我就不多解釋為什麼行不通,有各種不同的情形可以導致即使獲得焦點,isdiv3依然不會被設定為true,而且需要對每個html元素設定事件讓isdiv3變為false,這是很恐怖的事情).
這裡我放出一種比較通用和不容易被幹擾的解決辦法。
首先在 頁面的 最頂部寫上
<style>
div:focus{z-index:100;} // 這裡隨意你設定多少值,100隻是舉個列子
</style>
上面這個樣式告訴我們,當只有div 獲得焦點後 他會產生一個css屬性就是 z-index被設定成了100,以任何形式失去焦點 這個css屬性就沒了。當然你也可以設定其他的css屬性。因為我們在點button執行函數的時候,div3也會失去焦點(getSelection 依然存在)
以下思路就清晰了 我們再寫一個函數
var lastFocusID="";function getFocus() { var divlist = document.getElementsByTagName('div'); for(var i=0; i<divlist.length; i++) { var ta = divlist.item(i); if (window.getComputedStyle(ta, null).zIndex!=null && window.getComputedStyle(ta, null).zIndex == 100) { if(ta.id && ta.id!=null) lastFocusID=ta.id.toString(); else lastFocusID=""; break; } else lastFocusID=""; } } //再加入一個全屏事件 $(window).click(function(e) { if (window.getSelection) { var getevent=e.srcElement?e.srcElement:e.target;//不要告訴我不知道這句的意思 if(getevent.tagName=="INPUT" && getevent.id!=null && getevent.id=="cmdInsert") { //代表 點了插入html的按鈕 //則不執行getFocus方法 } else getFocus();//除非點了那個插入html的按鈕 其他時候必須要執行getFocus來更新最後失去焦點的div } }) 然後修改一下 insertHTML 這個方法
function insertHTML(html) { var dthis=$("#div3")[0]; var sel, range; if (window.getSelection) { if(lastFocusID!="div3") { $("#div3).html(dthis.innerHTML+html) ;//說明 使用者可能在其他控制項上 進行焦點或者其他動作 則 return;//後面不執行了
}
。。。。。。。。。。//其他代碼照舊
這樣就解決Firefox或者chrome裡面 會出現亂插入內容的現象。
當然這隻是一個思路, 歡迎大家提供更好的辦法和效能更高的思路。