2013-03-20 37 views
2

我試圖檢測何時一個表單輸入的值更改使用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中使用了撤銷/重做事件,但沒有找到有用的東西。有人可以幫助如何處理撤消/重做事件嗎?

+0

如果沒有別的,你可以檢查ctrl-z擊鍵,這是大多數系統上的撤銷序列。儘管如此,如果用戶在瀏覽器的菜單中執行了「編輯 - >撤消」,那麼也不知道是否會觸發。 – 2013-03-20 05:46:56

+0

爲什麼你不能在這裏使用切換功能...... !! – EnterJQ 2013-03-20 05:47:23

+0

@MarcB好點,捕捉擊鍵肯定會奏效。但是沒有辦法檢測'edit - > undo' – Ian 2013-03-20 05:48:43

回答

2

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 + ")"); 
}); 
+2

這永遠不會在Javascript中實現,至少始終如一(我可以看到瀏覽器中有怪癖),除非實際的撤銷/重做事件被合併。同時,捕獲ctrl + z鍵事件確實是唯一的可能性,但非常不完整。 (不是downvoter) – Ian 2013-03-20 05:54:53

+0

感謝您的出色反應!我會花時間閱讀代碼,但我想問一下,這樣做是否可以處理由撤銷或重做導致的更改? – 2013-03-20 06:01:29

+0

@czarpino - 我還沒有測試過如何使用瀏覽器的撤消功能,所以我不知道 - 你需要嘗試一下。如果按下撤銷鍵時焦點位於字段中,則應該處理該操作。但是,如果焦點在其他地方,並且用戶使用菜單撤消,我不知道。它似乎在jsFiddle中適用於我的Chrome,但我不知道您希望涵蓋的具體測試用例。 – jfriend00 2013-03-20 06:14:58

2

熱鍵由John Resig的(jQuery的作者)可以幫助

https://github.com/jeresig/jquery.hotkeys

自述文件

如果你想使用一個以上的調節劑(如Alt + Ctrl + z)你應該按照字母順序來定義它們,例如alt + ctrl + shift

+1

這是最接近/唯一的可能性。仍然不會捕獲'編輯 - >撤消' – Ian 2013-03-20 05:51:58

0
<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 
    }); 

更多信息是存儲先前輸入的值,並檢查新的輸入值等於前幾次之一。這並不完美(例如退格觸發它),效率稍低(見下一段),但你應該能夠得到你想要的結果。你可以查看代碼來撤消它,看看它實際上保留了什麼(我認爲它只是保持大多數輸入在彼此的時間範圍內保持丟失狀態),而不是保留所有以前的輸入。

0

曾經有一段時間,我在一個項目中需要類似的東西。對於我來說,標記的解決方案似乎並不優雅,無法得到結果。我使用了幾個在這裏回答的事情的組合來做到這一點。

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); 
    } 
}); 

我有這個working in action on codepen