Reputation: 4271
I have overridden the paste
event. I noticed that because the event's default behavior is prevented, it is not currently possible to undo the "paste" with Ctrl+Z.
$(this).on('paste', function (evt) {
// Get the pasted data via the Clipboard API.
// evt.originalEvent must be used because this is jQuery, not pure JS.
// https://stackoverflow.com/a/29831598
var clipboardData = evt.originalEvent.clipboardData || window.clipboardData;
var pastedData = clipboardData.getData('text/plain');
// Trim the data and set the value.
$(this).val($.trim(pastedData));
// Prevent the data from actually being pasted.
evt.preventDefault();
});
Is there a way to override the undo functionality or do the above differently such that Ctrl+Z will work?
Upvotes: 12
Views: 2349
Reputation: 4271
I found a way to make it work. Starting with this answer, I changed it to use .focus()
instead of .select()
, which fixes the pasting. Then, to make pasting work in Firefox, I had to keep the fallback that doesn't preserve undo history. This will have to do until Firefox fixes the bug (See bug report).
Update: The Firefox bug was fixed in Firefox 89
function insertAtCaretTrim(element, text) {
element[0].focus();
// Attempt to preserve edit history for undo.
var inserted = document.execCommand("insertText", false, $.trim(text));
// Fallback if execCommand is not supported.
if (!inserted) {
var caretPos = element[0].selectionStart;
var value = element.val();
// Get text before and after current selection.
var prefix = value.substring(0, caretPos);
var suffix = value.substring(element[0].selectionEnd, value.length);
// Overwrite selected text with pasted text and trim. Limit to maxlength.
element.val((prefix + $.trim(text) + suffix).substring(0, element.attr('maxlength')));
// Set the cursor position to the end of the paste.
caretPos += text.length;
element.focus();
element[0].setSelectionRange(caretPos, caretPos);
}
}
var $inputs = $("input");
$inputs.each(function () {
$(this).on('paste', function (evt) {
var clipboardData = evt.originalEvent.clipboardData || window.clipboardData;
var pastedData = clipboardData.getData('text/plain');
// Trim the data and set the value.
insertAtCaretTrim($(this), pastedData);
evt.preventDefault();
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" maxvalue="10" />
Code is also in a JSFIddle: https://jsfiddle.net/mf8v97en/5/
Upvotes: 1
Reputation: 17556
Use
document.execCommand("insertText", false, $.trim(pastedData));
instead of
$(this).val($.trim(pastedData));
It will preserve the undo history.
$('#inputElement').on('paste', function (evt) {
var clipboardData = evt.originalEvent.clipboardData || window.clipboardData;
var pastedData = clipboardData.getData('text/plain');
this.select(); // To replace the entire text
document.execCommand("insertText", false, $.trim(pastedData));
evt.preventDefault();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea id="inputElement"></textarea>
Upvotes: 7
Reputation: 10400
A possible solution is to implement an undo stack manually. The algorithm would be something like:
input
events that pushes a new entry on an "undo stack" when the input is different from the input of the last input stack element. This listener should at a minimum be debounced to avoid single-letter undo stack elements.paste
event listener also pushes an entry on the undo stack when invoked.keydown
listener that intercepts CTRL-Z
and pops the last entry from the undo stack.It sure seems like a lot of work for something that is already built-in to the browsers so I'm hoping there's a better solution.
Upvotes: 3