Fix IE6 剪贴板撤销机制CtrlZ,CtrlY功能会在由于Js动态改变页面元素的value后失效的Problem

这个问题是在我们的后台的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纪录光标的位置,具体代码如下

<html>

<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([
00, 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 [00, 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功能正常

原文地址:https://www.cnblogs.com/xueduanyang/p/1385128.html