如何在JavaScript中处理撤销/重做事件?
我试图检测何时一个表单输入的值更改使用JavaScript & JQuery。不幸的是,我发现JQuery的$(elem).change()
不足,因为它只在elem
失去焦点时触发更改事件。我必须立即知道表格输入值有变化时。为此,我已经缩小了与输入的值可能发生变化相关联KEYUP,贴,切,撤消和重做事件。但是,JavaScript和JQuery似乎都没有办法处理撤销或重做。如何在JavaScript中处理撤销/重做事件?
var onChange = function()
{
alert('Checking for changes...');
};
$(this).off('keyup').on('keyup', onChange);
$(this).off('paste').on('paste', onChange);
$(this).off('cut').on('cut', onChange);
$(this).off('undo').on('undo', onChange); // undo ?
$(this).off('redo').on('redo', onChange); // redo ?
我已经在Javascript/JQuery中使用了撤销/重做事件,但没有找到有用的东西。有人可以帮助如何处理撤消/重做事件吗?
JavaScript中没有撤消或重做事件。如果你想要这样的功能,你必须自己写在JavaScript中或找到一个提供这种功能的库。
如果您试图捕捉所有可能的方式,即可以更改输入控件,以便您可以立即看到此类更改,请查看以下示例代码:http://jsfiddle.net/jfriend00/6qyS6/,该代码为输入控件实现了更改回调。这段代码并不是直接为下拉菜单设计的,但由于它是一种输入控件的形式,因此您可以调整此代码为下拉菜单创建自己的更改事件。
那么,StackOverflow在他们的无限智慧是禁止我只发布一个jsFiddle的引用,所以我必须粘贴在这里的所有代码(出于某种原因,jsFiddles单独出来,而不是其他网站引用)。我不是代表这是一个确切的解决方案,但作为一个模板,你可以使用如何检测用户更改输入控制:
(function($) {
var isIE = false;
// conditional compilation which tells us if this is IE
/*@cc_on
isIE = true;
@*/
// Events to monitor if 'input' event is not supported
// The boolean value is whether we have to
// re-check after the event with a setTimeout()
var events = [
"keyup", false,
"blur", false,
"focus", false,
"drop", true,
"change", false,
"input", false,
"textInput", false,
"paste", true,
"cut", true,
"copy", true,
"contextmenu", true
];
// Test if the input event is supported
// It's too buggy in IE so we never rely on it in IE
if (!isIE) {
var el = document.createElement("input");
var gotInput = ("oninput" in el);
if (!gotInput) {
el.setAttribute("oninput", 'return;');
gotInput = typeof el["oninput"] == 'function';
}
el = null;
// if 'input' event is supported, then use a smaller
// set of events
if (gotInput) {
events = [
"input", false,
"textInput", false
];
}
}
$.fn.userChange = function(fn, data) {
function checkNotify(e, delay) {
// debugging code
if ($("#logAll").prop("checked")) {
log('checkNotify - ' + e.type);
}
var self = this;
var this$ = $(this);
if (this.value !== this$.data("priorValue")) {
this$.data("priorValue", this.value);
fn.call(this, e, data);
} else if (delay) {
// The actual data change happens after some events
// so we queue a check for after.
// We need a copy of e for setTimeout() because the real e
// may be overwritten before the setTimeout() fires
var eCopy = $.extend({}, e);
setTimeout(function() {checkNotify.call(self, eCopy, false)}, 1);
}
}
// hook up event handlers for each item in this jQuery object
// and remember initial value
this.each(function() {
var this$ = $(this).data("priorValue", this.value);
for (var i = 0; i < events.length; i+=2) {
(function(i) {
this$.on(events[i], function(e) {
checkNotify.call(this, e, events[i+1]);
});
})(i);
}
});
}
})(jQuery);
function log(x) {
jQuery("#log").append("<div>" + x + "</div>");
}
// hook up our test engine
$("#clear").click(function() {
$("#log").html("");
});
$("#container input").userChange(function(e) {
log("change - " + e.type + " (" + this.value + ")");
});
这永远不会在Javascript中实现,至少始终如一(我可以看到浏览器中有怪癖),除非实际的撤销/重做事件被合并。同时,捕获ctrl + z键事件确实是唯一的可能性,但非常不完整。 (不是downvoter) – Ian 2013-03-20 05:54:53
感谢您的出色反应!我会花时间阅读代码,但我想问一下,这样做是否可以处理由撤销或重做导致的更改? – 2013-03-20 06:01:29
@czarpino - 我还没有测试过如何使用浏览器的撤消功能,所以我不知道 - 你需要尝试一下。如果按下撤销键时焦点位于字段中,则应该处理该操作。但是,如果焦点在其他地方,并且用户使用菜单撤消,我不知道。它似乎在jsFiddle中适用于我的Chrome,但我不知道您希望涵盖的具体测试用例。 – jfriend00 2013-03-20 06:14:58
热键由John Resig的(jQuery的作者)可以帮助
https://github.com/jeresig/jquery.hotkeys
自述文件
如果你想使用一个以上的调节剂(如Alt + Ctrl + z)你应该按照字母顺序来定义它们,例如alt + ctrl + shift
这是最接近/唯一的可能性。仍然不会捕获'编辑 - >撤消' – Ian 2013-03-20 05:51:58
您可以监视使用MutationObserver的所有更改。这不会为每个keydown和keyup事件提供事件,但它可以合并多个更改并作为单个事件发给您。约MutationObserver https://developer.mozilla.org/en/docs/Web/API/MutationObserver
<input type="text"/>
<script>
var newInput = "";
var oldInput = [$('input').val()];
$('input').on('input',function(){
newInput = $(this).val();
redo = false;
$(oldInput).each(function(i){if(newInput==oldInput[i]){redo = true; return false});
if(redo){
console.log('do code for an undo or redo');
}
oldInput.push(newInput);
console.log([oldInput,newInput]);
});
</script>
的基本概念
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// mutation.target will give you element which has been modified.
// mutation.addedNodes and mutation.removedNodes will give you operations that were performed on the node
// happy coding :)
}
});
});
observer.observe(elementsToMonitor, {
attributes: true,
childList: true,
characterData: true
});
更多信息是存储先前输入的值,并检查新的输入值等于前几次之一。这并不完美(例如退格触发它),效率稍低(见下一段),但你应该能够得到你想要的结果。你可以查看代码来撤消它,看看它实际上保留了什么(我认为它只是保持大多数输入在彼此的时间范围内保持丢失状态),而不是保留所有以前的输入。
曾经有一段时间,我在一个项目中需要类似的东西。对于我来说,标记的解决方案似乎并不优雅,无法得到结果。我使用了几个在这里回答的事情的组合来做到这一点。
function UndoListener(options){
if(!options.el instanceof HTMLElement) return;
this.el = options.el;
this.callback = options.callback || function(){};
this.expectedChange = false;
this.init();
}
UndoListener.prototype = {
constructor: UndoListener,
addListeners: function(){
this.el.addEventListener('keydown', (e) => this.expectedChange = this.eventChecker(e));
this.el.addEventListener('cut', (e) => this.expectedChange = true);
this.el.addEventListener('paste', (e) => this.expectedChange = true);
},
addObserver: function(){
this.observer = new MutationObserver((mt) => {
if(!this.expectedChange){
this.expectedChange = true;
this.observer.disconnect();
this.callback.call(this.el, {
original: [...mt].shift().oldValue,
current: this.el.innerText
});
this.addObserver();
}
this.expectedChange = false;
});
this.observer.observe(this.el, {
characterData: true,
subtree: true,
characterDataOldValue: true
});
},
eventChecker: function(event) {
return !(~['z','y'].indexOf(event.key) && (event.ctrlKey || event.metaKey));
},
init: function(){
this.addListeners();
this.addObserver();
}
}
这使用MutationObserver“catch”撤消事件。它这样做是因为MutationObserver在事件触发后触发。我们检查事件是否是预期事件,如keydown或cut,并允许在没有回调触发的情况下进行更改。如果事件是意外的,我们假设发生了撤消。这不能区分撤销和重做;回调也会触发。用法:
var catcher = new UndoListener({
el: document.querySelector('.container'),
callback: function(val){
console.log('callback fired', val);
}
});
如果没有别的,你可以检查ctrl-z击键,这是大多数系统上的撤销序列。尽管如此,如果用户在浏览器的菜单中执行了“编辑 - >撤消”,那么也不知道是否会触发。 – 2013-03-20 05:46:56
为什么你不能在这里使用切换功能...... !! – EnterJQ 2013-03-20 05:47:23
@MarcB好点,捕捉击键肯定会奏效。但是没有办法检测'edit - > undo' – Ian 2013-03-20 05:48:43