这个问题是在我们的后台的CMS系统中发现的,由于编辑人员要大量使用Js对于Textarea和Input Text中的内容作处理,当有Js改变了Input Text的value后,textarea中undo功能就失效了,这个问题是致命的。
经过我们检查,此问题只发现在IE6,而FF中是没有这个问题的,问题的原因,我们猜测是因为IE6的Undo是绑定在Page对象上的,当Js改变DOM结构或者改变了El的value,该对象不知道为什么也被重置了,而FF是将Undo功能绑定在具体每个元素上的,所以要Fix这个问题,只能采用Js模拟FF将Undo的ClipBoard绑定在每个元素上了。
方法如下,我们覆盖了IE默认的Ctrl-Z和Ctrl-Y功能,用一个长度固定的Queue存放文本区每次改变的值,因为输入文本存在连续输入的可能,所以我们作了一个延时器,当1秒后再无任何动作后,才将value值保存,同时IE在每次Undo的时候都会将光标位置保留,所以我们又用了一个同样的Queue纪录光标的位置,具体代码如下
< head >
< meta http - equiv = " Content-Type " content = " text/html; charset=gb2312 " >
< title > test < / title>
< / head>
< body >
< input id = " txt1 " type = " text " value = "" size = " 44 " >
< input type = " button " value = " click me 1 " onclick = " clickMe1() " >
< script >
function clickMe1(){
document.getElementById( ' txt1 ' ).value = ' 4545 ' ;
}
< / script>
< br / >
< textarea id = " txt2 " rows = " 24 " cols = " 57 " >< / textarea>
< input type = " button " value = " click me 2 " onclick = " clickMe2() " >
< script >
function clickMe2(){
document.getElementById( ' txt2 ' ).value = ' 4545 ' ;
}
< / script>
< br / >
< input type = " button " value = " Apply Js Undo Function Fix IE6 Bug " onclick = " fixUndo() " >
< script >
function fixUndo(){
fixIE6Undo(document.getElementById( ' txt1 ' ),document.getElementById( ' txt2 ' ));
}
function fixIE6Undo(){
// Valid when UA is IE and version is less than 7
if ( ! ( / MSIE (\d+\.\d) / .exec(navigator.userAgent)[ 1 ] <= 6.0 )) {
return false ;
}
var debugMode = false ;
var debugPrint = function (s){
var d = document.createElement( ' div ' );
d.innerHTML = s;
document.body.appendChild(d);
};
// Set global Timer
var _undoTimer = null ;
// Set Undo max step
var _maxUndoStep = 20 ;
var setBookmark = function (e, bookmarks){
e.scrollTop = bookmarks[ 2 ];
var end = bookmarks[ 1 ];
var start = bookmarks[ 0 ];
// Fix IE offset bug with char return&newline
var ose = e.value.substring( 0 , end).split( / \n / g).length - 1 ;
var ost = e.value.substring( 0 , start).split( / \n / g).length - 1 ;
// Resume Range
var range = e.createTextRange();
range.collapse( true );
range.moveEnd( ' character ' , end - ose);
range.moveStart( ' character ' , start - ost);
range.select();
e.focus();
};
var applyUndoStep = function (e){
e.value = e._undoQueue[e._undoQueueIndex];
var bookmarks = e._bookmarkQueue[e._undoQueueIndex];
if ( ' TEXTAREA ' == e.tagName) {
setBookmark(e, bookmarks);
}
if (debugMode) {
debugPrint(e._undoQueueIndex + ' - ' + e._undoQueue[e._undoQueueIndex] + ' <hr /> ' );
}
};
for ( var i = 0 ; i < arguments.length; i ++ ) {
// Deal with each argument
( function (el){
if (( ' INPUT ' == el.tagName && ' text ' == el.type) || ' TEXTAREA ' == el.tagName) {
el._undoQueue = new Array();
el._bookmarkQueue = new Array();
el._undoQueueIndex = 0 ;
el._undoQueue.push(el.value || '' );
el._bookmarkQueue.push([ 0 , 0 , el.scrollTop]);
el.attachEvent( ' onpropertychange ' , function (){
if (window.event && window.event.propertyName == ' value ' ) {
clearTimeout(_undoTimer);
_undoTimer = setTimeout( function (){
var uQue = el._undoQueue;
var bQue = el._bookmarkQueue;
if (el.value != uQue[el._undoQueueIndex]) {
// Value changed
el._undoQueueIndex ++ ;
var len = uQue.length - el._undoQueueIndex;
if (len > 0 ) {
// Slice undo queue
uQue.splice(el._undoQueueIndex, len);
bQue.splice(el._undoQueueIndex, len);
}
if (uQue.length >= _maxUndoStep) {
// Reached max step
uQue.shift();
bQue.shift();
el._undoQueueIndex -- ;
}
var bookmarks = ( function (e){
e.focus();
var r = document.selection.createRange();
if ( ! r) {
return [ 0 , 0 , e.scrollTop];
}
var re = e.createTextRange();
var rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint( ' EndToStart ' , re);
return [rc.text.length, rc.text.length + r.text.length, e.scrollTop];
})(el);
// Add new value to undo queue
uQue.push(el.value);
bQue.push(bookmarks);
}
}, 300 );
}
});
el.attachEvent( ' onkeydown ' , function (){
if ( !! event.ctrlKey) {
if (event.keyCode == 90 ) {
// Undo with ctrl + z
if (el._undoQueueIndex > 0 ) {
el._undoQueueIndex -- ;
applyUndoStep(el);
}
return false ;
}
else {
if (event.keyCode == 89 ) {
// Redo with ctrl + y
if (el._undoQueueIndex + 1 < el._undoQueue.length) {
el._undoQueueIndex ++ ;
applyUndoStep(el);
}
return false ;
}
}
}
});
}
})(arguments[i]);
}
}
< / script>
< / body>
< / html>
测试JS内存性能结果:
1.打开测试网页,内存占用为11,740
2.让页面任意元素获得焦点,内存占用开始小幅波动,峰值约为12,300
3.点击Apply..Js按钮,应用FixIE6Undo功能,内存降为12,624
4.在文本区反复输入长度为6521字符的文本,次数10次,重复执行Ctrl-C,Ctrl-V操作,内存占用为20,388
5.在文本区反复使用Ctrl-Y,Ctrl-Z功能,内存占用逐步增长,到42,700左右,开始微幅下滑,始终在42,650左右浮动,内存并未出现溢出现象,且经过反复测试Ctrl-Z,Ctrl-Y功能正常