3

I have some code implementing a context menu on a textbox, the context menu is to have an Undo and Redo item that calls the browsers native methods by using document.execCommand('undo').

This code functions as I require on Chromium based browsers but on FireFox and Opera the results are not as expected.

My expectation is that undo and redo will function like the native browser context menu for an input element. The result is that the input elements do not undo and redo, however div elements with the contenteditable attribute set, do function as expected.

So I'm wondering if this is a bug in one of the browsers, either Chromium or FireFox/Opera, or if I am not implementing the code correctly?

The following code gives an example of the issue that I'm facing. All help is appreciated.

<input contenteditable id="input" type="text"></input>
<div contenteditable id="div" class="inputLike" type="text"></div>
<button id="button1" type="button">Undo</button>
<button id="button2" type="button">Redo</button>

var input = document.getElementById("input"),
    button1 = document.getElementById("button1"),
    button2 = document.getElementById("button2"),
    div = document.getElementById("div");

console.log("Undo", document.queryCommandSupported("undo"));
console.log("Redo", document.queryCommandSupported("redo"));

function doUndo() {
    document.execCommand('undo', false, null);
}

function doRedo() {
    document.execCommand('redo', false, null);
}

button1.addEventListener("click", doUndo, false);
button2.addEventListener("click", doRedo, false);

On jsfiddle

If you want to look at the actual context menu code, then it is also available on jsfiddle.

Xotic750
  • 22,914
  • 8
  • 57
  • 79
  • I think that `execCommand()` is only supported for "contenteditable" sections in Firefox, and not for `` elements. – Pointy Apr 25 '13 at 22:27
  • I've tried to search for some hard documentation about this but haven't been able to find anything concrete. If you know of any or any other possible methods? – Xotic750 Apr 25 '13 at 22:37
  • Well all I know is that `execCommand()` only shows up on MDN in the documentation for "rich editing". – Pointy Apr 25 '13 at 22:40
  • Hello! I don't think it's possible with `document.execCommand()`, in Firefox at least. – Tim Down Apr 26 '13 at 09:05

3 Answers3

2

I don't think it's possible with document.execCommand(), in Firefox at least. You could make your own undo stack, or in future use the new UndoManager API (implemented in Firefox 20 but disabled by default).

Here's an example of using your own undo stack by taking snapshots of the value and selection using the input event. You could improve this by merging consecutive typing events into a single undo item, for example. There is also some inconsistency between browsers with the caret position, but it's just a proof of concept.

http://jsfiddle.net/FMSsL/

Using the new DOM UndoManager API seems to be simple: if I understand it right and if the browser supports it, the <input> element will have an undoManager property, which is an object with undo() and redo() methods, so the task is as simple as

document.getElementById("input").undoManager.undo();

Unfortunately only Firefox 20 and above supports the UndoManager API and it's disabled by default. Even once it's enabled, the following demo does not work even though I think it should, so this option is some way off being viable.

http://jsfiddle.net/DULV4/2/

Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • Thanks for the answer. It's a pity that all the browsers do not act in the same manner, which one has implemented incorrectly (or maybe just interpretation) I don't know. For now, I guess I will wait until this new API comes along, unless I can find some kind of shim for it so that I can start looking at it now. – Xotic750 Apr 26 '13 at 15:12
1

As I discovered from the question I asked, the undoManager API in Firefox DOES work. I looked at the jsFiddle link (http://jsfiddle.net/DULV4/1/) posted by Tim Down, and there appear to be a couple issues:

  • An undoScope attribute must be set to true (either in-line or programmatically). This enables the undoManager for that element.
  • Anything you undo has to first be created by the undoManager.transact() function (though I'm wondering if there is any way to incorporate the native undo stack into the current undoManager's transaction history).

I'm only a novice with this, so take what I say with a grain of salt and see https://dvcs.w3.org/hg/undomanager/raw-file/tip/undomanager.html for all the information on using it.

Community
  • 1
  • 1
Daniel H
  • 443
  • 3
  • 14
  • Thanks for the information, I had postponed looking into this but I may change my mind and revisit it soon. – Xotic750 May 05 '13 at 19:46
  • You're right, I definitely was missing an `undoScope` attribute in my example. As to the rest, the spec doesn't seem to have inputs and textareas in mind, so it's unclear to me how or even whether it should be possible to undo user input using its `UndoManager`. – Tim Down May 06 '13 at 10:16
  • @tim-down It seems that anything you want to undo has to first be done with the `transact()` function, so for the `undoManager` to work with a ` – Daniel H May 06 '13 at 19:26
  • My interpretation is that automatic DOM transactions should be added to the undo transaction history automatically and `transact()` could be used with `executeAutomatic` to execute some custom action when an automatic transaction is executed, and that Firefox simply hasn't implemented the automatic transactions part of the spec yet. I could well be wrong though. – Tim Down May 06 '13 at 20:59
  • I will go over the spec properly soon: I think I may be making false assumptions. – Tim Down May 06 '13 at 23:17
  • Javascript UndoManager Browser Support **NO** ... see https://stackoverflow.com/q/2858919/1066234 – Avatar May 29 '22 at 13:50
0

IT IS POSSIBLE TO GET HISTORY OF THE UNDO AND REDO

function check(){
if(document.queryCommandEnabled("undo"))
  {
    $('#undoResult').text("Undo is active");
    }else{
      $('#undoResult').text("Undo is not active");
  }
if(document.queryCommandEnabled("redo"))
  {
    $('#redoResult').text("Redo is active");
    }else{
      $('#redoResult').text("Redo is not active");
  }
  }
  $(document).on('keypress',function(e) {
      if(e.which == 13) {
        document.execCommand("insertLineBreak");
              return false;
      }
      });
  check();
div{
  border:1px solid black;
  height:100px;
}

button{
color:white;
background:black;
height:40px;
width:49%;
padding:1px;
text-align:center;
margin-top:10px;
}

p{
font-size:30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div contenteditable="true">
</div>

<button onclick="document.execCommand('undo',false,null);check()" >Undo</button>

<button onclick="document.execCommand('redo',false,null); check()" >Redo</button>

<p id='undoResult'></p>
<p id='redoResult'></p>
  • Please refrain from writing text all in capital letters, this is not an IRC chat room... Also, please provide explanation & relevant links to the commands used here (`queryCommandEnabled` for example). Also note that `execCommand` has been **Deprecated** for a long time, and your answer shows you did not bother to check that... – vsync Nov 21 '22 at 09:32