【翻译作品】JavaScript DOM学习第二章:编辑文本

在这一章我会给出一个在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

posted @ 2010-02-18 14:04  BeiYuu  阅读(501)  评论(0编辑  收藏  举报