文章目錄
在這一章我會給出一個在CMS裡非常有用的更新頁面的代碼。在任一段落點擊滑鼠你就可以修改了。完成以後點擊按鈕,修改的文本就顯示了。
例子
這個頁面就是個例子。點擊一個段落,編輯,然後點Ready。你的修改就會呈現。
問題
遇到的第一個問題是:我想用文字框作為編輯地區。一開始我卻把內容放不進文字框去。讀者發現Mozilla的一個警告說是只有在文字框放置到文檔之後才能設定它的value。
另外,在Mozilla下面內容封裝的不是很好。我試了好幾種wrap參數,但是結果都不是很好。
最嚴重的問題就是把修改後的內容發回伺服器,這是幾乎所有的CMS系統都要做的。讀者給了我很多高明巧妙的建議。然而因為不能通過JavaScript完成,所以我也不能提供解決辦法。所以也請您不要發郵件告訴你找到了辦法:那也許可行,但是我只想要純JavaScript的不需要伺服器端代碼的方法。
指令碼
var editing = false;if (document.getElementById && document.createElement) {var butt = document.createElement('BUTTON');var buttext = document.createTextNode('Ready!');butt.appendChild(buttext);butt.onclick = saveEdit;}function catchIt(e) {if (editing) return;if (!document.getElementById || !document.createElement) return;if (!e) var obj = window.event.srcElement;else var obj = e.target;while (obj.nodeType != 1) {obj = obj.parentNode;}if (obj.tagName == 'TEXTAREA' || obj.tagName == 'A') return;while (obj.nodeName != 'P' && obj.nodeName != 'HTML') {obj = obj.parentNode;}if (obj.nodeName == 'HTML') return;var x = obj.innerHTML;var y = document.createElement('TEXTAREA');var z = obj.parentNode;z.insertBefore(y,obj);z.insertBefore(butt,obj);z.removeChild(obj);y.value = x;y.focus();editing = true;}function saveEdit() {var area = document.getElementsByTagName('TEXTAREA')[0];var y = document.createElement('P');var z = area.parentNode;y.innerHTML = area.value;z.insertBefore(y,area);z.removeChild(area);z.removeChild(document.getElementsByTagName('button')[0]);editing = false;}document.onclick = catchIt;
解釋我們設定一個editing標誌為false。這用來顯示使用者是否正在編輯段落。當然初始是沒有。
var editing=false;
建立一個按鈕然後我們建立一個Radey按鈕,後面會需要很多次。這需要一些進階指令碼技術,所以先做一些對象檢測:
if (document.getElementById && document.createElement) {
如果是現代瀏覽器,則建立按鈕:
var butt = document.createElement('BUTTON');
他的文本是:
var buttext = document.createTextNode('Ready!');
把這個文本添加到按鈕上:
butt.appendChild(buttext);
然後添加一個onclick事件處理常式:
butt.onclick = saveEdit;}
現在按鈕就儲存在butt裡面,需要的時候我們就可以直接引用。
將P轉為文字框稍後我們會為整個頁面定義一個onclick事件。所有的這些事件都會發送到catchIt()函數。
function catchIt(e){
首先檢測使用者是否正常編輯段落,如果是,結束函數:
if (editing) return;
然後是支援性檢測:
if (!document.getElementById || !document.createElement) return;
然後尋找事件的源:
if (!e) var obj = window.event.srcElement;else var obj = e.target;
現在我們有了事件的源,但是有個問題是Mozilla會認為文本節點是源(而不是我們需要的P節點)。所以如果節點不是標籤(nodeType不是1),我們需要向上遍曆DOM樹:
while (obj.nodeType != 1) {obj = obj.parentNode;}
現在我們以一個標籤結束。如果這是一個文字框的標籤那麼使用者點擊之後就可以編輯了。如果是一個連結的標籤那麼使用者點擊之後應該還是作為一個連結來反映的。這兩種情況下我們就不需要這個函數了:
if (obj.tagName == 'TEXTAREA' || obj.tagName == 'A') return;
我們需要再一次的向上遍曆DOM樹直到找到P標籤或者HTML標籤:
while (obj.nodeName != 'P' && obj.nodeName != 'HTML') {obj = obj.parentNode;}
如果是HTML標籤那麼表示使用者在段落之外點擊的,就結束函數:
if (obj.nodeName == 'HTML') return;
經過這個檢測我們最終確定使用者點擊的是我們想要編輯的段落。然後儲存段落的innerHTML:
var x = obj.innerHTML;
建立一個新的TEXTAREA然後儲存:
var y = document.createElement('TEXTAREA');
然後找到段落的父節點:
var z = obj.parentNode;
現在的情形是:
z | --------------------------------------- | | | [more] P [more]
然後通過父節點,在段落之前插入新的TEXTAREA節點:
z.insertBefore(y,obj);
同樣的插入Ready按鈕:
z.insertBefore(butt,obj);
現在就成了這樣:
z | --------------------------------------- | | | | | [more] y(TEXTAREA) butt(BUTTON) P [more]
然後刪除段落。現在看起來就好像是文字框和按鈕代替了之前的段落。
直到現在,插入文字框之後,我們才能把段落的innerHTML放置在文字框內。Mozilla裡面不支援在插入之前給文字框內新增內容。
y.value = x;
為了使用者方便給文字框焦點:
y.focus();
然後設定editing為true。
editing = true;}
將文字框轉換為P當使用者點擊Ready按鈕,就應該反過來了。這個由saveEdit()函數來完成。
function saveEdit() {
得到TEXTAREA(這裡假設整個頁面只有一個TEXTAREA):
var area = document.getElementsByTagName('TEXTAREA')[0]
建立一個新的段落並儲存:
var y = document.createElement('P');
找到文字框的父元素:新的段落需要添加到那去:
var z = area.parentNode;
將文字框的值儲存在新的段落裡:
y.innerHTML = area.value;
然後把新的段落插入在文字框之前:
z.insertBefore(y,area);
移除文字框:
z.removeChild(area);
移除Ready按鈕(同樣的,假設頁面只有一個按鈕):
z.removeChild(document.getElementsByTagName('button')[0]);
然後設定editing為false:使用者停止編輯:
editing = false;}
事件
在函數之外,設定一個整個頁面的onclick事件:
document.onclick = catchIt;
翻譯地址:http://www.quirksmode.org/dom/cms.html