msalamon
asked on
Undo and Only Allow Deletes in Textarea
Delete Only
I need to create a text area that will provide very limited editing capabilities to our users. The textarea will come prefilled with text. I want to give users the ability to ONLY delete text from the textarea, not to add any text. I have generally figured out how to do this, by disabling all keys except delete/backspace and the arrow keys. One thing I have not been able to turn off, however, it the right-mouse-click paste feature, which will paste text, notwithstanding the disabling of other keys. I want the paste disabled JUST for the textarea (there will be other stuff on the page).
Undo
Related to this, I want to give users the ability to undo (redo is not important) any deletions they have made, such as by including an undo button under the textarea. (there can be an unlimited number of undos). However, I have not been able to figure out how to do this.
I need to create a text area that will provide very limited editing capabilities to our users. The textarea will come prefilled with text. I want to give users the ability to ONLY delete text from the textarea, not to add any text. I have generally figured out how to do this, by disabling all keys except delete/backspace and the arrow keys. One thing I have not been able to turn off, however, it the right-mouse-click paste feature, which will paste text, notwithstanding the disabling of other keys. I want the paste disabled JUST for the textarea (there will be other stuff on the page).
Undo
Related to this, I want to give users the ability to undo (redo is not important) any deletions they have made, such as by including an undo button under the textarea. (there can be an unlimited number of undos). However, I have not been able to figure out how to do this.
undoes
<script>
var versions = new Array();
</script>
<textarea id=youcannottypetome
onbeforepaste=event.return Value=fals e
onpaste=event.returnValue= false
onkeydown=(hoping you killed all keys on keydown)
onkeyup=versions[versions. length]=th is.value
oncut=versions[versions.le ngth]=this .value
>HIHI</textarea>
<button onclick="youcannottypetome .value=ver sions[vers ions.lengt h-1];versi ons.length =versions. length-1"
>HIHI</button>
<script>
var versions = new Array();
</script>
<textarea id=youcannottypetome
onbeforepaste=event.return
onpaste=event.returnValue=
onkeydown=(hoping you killed all keys on keydown)
onkeyup=versions[versions.
oncut=versions[versions.le
>HIHI</textarea>
<button onclick="youcannottypetome
>HIHI</button>
ASKER
Thanks. The paste restriction worked great. However, I have a few concerns about the undo.
First, the restriction on editing that I need, that you can only delete, does not seem to work if you fully disable onkeydown, which you recommended. I am not an expert on javascript in any way, but I was applying the same function:
function disableNonDelete()
{
e = event;
if(e.keyCode == 8 || e.keyCode == 46 || (e.keyCode >= 37 && e.keyCode <=40))
return true;
else
return false;
}
on each of the following (although all may not be necessary):
onKeyDown="return disableNonDelete();"
onKeyPress="return disableNonDelete();"
onKeyUp="return disableNonDelete();"
onChange="return disableNonDelete();"
How can I both preserve the ability to delete with the delete/backspace key and include ALL deletes (using either delete, backspace, control-x or right-mouse-cut) in the undo functionality.
Second, my disable delete function does not seem to be able to allow control-x for deleting. Since this is a key combination, as soon as the control key is hit, the function won't wait for the -x to be hit as well. Do you know how I can get control-x to work too (along with delete, backspace, and the arrows)?
Third, if you hit undo and there is nothing in the array, then the textarea gets the text "undefined". How do I disable the undo button when the array.length = 0?
Fourth, the Array holds the entire contents of the text area, which in some cases may be 20+ pages. If we store a copy of the text every time a single letter is deleted, the array is going to get VERY full What I was hoping (but couldn't figure out how), was to use the document.getSection() methods of javascript to work. I was hoping that each time you either selected and then deleted text, or just deleted text with a key stroke, that I could store the starting point of where the delete happened and the text deleted, and put that into the undo array. Then, each time you hit undo, it would take the last deletion and insert the text at the point where it was deleted, updating the text in the textarea. this would greatly reduce the text stored in the array. A solution like that would be great!
First, the restriction on editing that I need, that you can only delete, does not seem to work if you fully disable onkeydown, which you recommended. I am not an expert on javascript in any way, but I was applying the same function:
function disableNonDelete()
{
e = event;
if(e.keyCode == 8 || e.keyCode == 46 || (e.keyCode >= 37 && e.keyCode <=40))
return true;
else
return false;
}
on each of the following (although all may not be necessary):
onKeyDown="return disableNonDelete();"
onKeyPress="return disableNonDelete();"
onKeyUp="return disableNonDelete();"
onChange="return disableNonDelete();"
How can I both preserve the ability to delete with the delete/backspace key and include ALL deletes (using either delete, backspace, control-x or right-mouse-cut) in the undo functionality.
Second, my disable delete function does not seem to be able to allow control-x for deleting. Since this is a key combination, as soon as the control key is hit, the function won't wait for the -x to be hit as well. Do you know how I can get control-x to work too (along with delete, backspace, and the arrows)?
Third, if you hit undo and there is nothing in the array, then the textarea gets the text "undefined". How do I disable the undo button when the array.length = 0?
Fourth, the Array holds the entire contents of the text area, which in some cases may be 20+ pages. If we store a copy of the text every time a single letter is deleted, the array is going to get VERY full What I was hoping (but couldn't figure out how), was to use the document.getSection() methods of javascript to work. I was hoping that each time you either selected and then deleted text, or just deleted text with a key stroke, that I could store the starting point of where the delete happened and the text deleted, and put that into the undo array. Then, each time you hit undo, it would take the last deletion and insert the text at the point where it was deleted, updating the text in the textarea. this would greatly reduce the text stored in the array. A solution like that would be great!
oopsie wrong event for the control+X and mouse cut ; this will be better (oncut should be onbeforecut :)
<script>
var versions = new Array();
function undo() {
if(version.length>0) {
youcannottypetome.value=ve rsions[ver sions.leng th-1];
versions.length=versions.l ength-1;
}
}
function keydow() {
switch(event.keyCode) {
case 88: case 67: // x and c (not sure if you want c because that is copy not really delete)
if(event.ctrlKey) return; // if control x or control c go about yer business break;
case 8: case 46:
return; // if del or backspace go about your business
break;
}
event.returnValue = false; // everything else is canceled
}
</script>
<textarea id=youcannottypetome
onbeforepaste=event.return Value=fals e
onpaste=event.returnValue= false
onkeydown=keydown()
onkeyup=versions[versions. length]=th is.value
onbeforecut=versions[versi ons.length ]=this.val ue
>HIHI</textarea>
<button onclick="youcannottypetome .value onclick=undo()>HIHI</butto n>
note if you allow them to use copy/control c you need to add
onbeforecopy=versions[vers ions.lengt h]=this.va lue
-w
<script>
var versions = new Array();
function undo() {
if(version.length>0) {
youcannottypetome.value=ve
versions.length=versions.l
}
}
function keydow() {
switch(event.keyCode) {
case 88: case 67: // x and c (not sure if you want c because that is copy not really delete)
if(event.ctrlKey) return; // if control x or control c go about yer business break;
case 8: case 46:
return; // if del or backspace go about your business
break;
}
event.returnValue = false; // everything else is canceled
}
</script>
<textarea id=youcannottypetome
onbeforepaste=event.return
onpaste=event.returnValue=
onkeydown=keydown()
onkeyup=versions[versions.
onbeforecut=versions[versi
>HIHI</textarea>
<button onclick="youcannottypetome
note if you allow them to use copy/control c you need to add
onbeforecopy=versions[vers
-w
for the selection area the hardest part you are going to have is finding where you are in the textarea, i think ns6 has a cursorlocation call but ie doesnt.
here is the psudo of what you need to do
on before delete key or backspace key
var selection = document.selection.createR ange().dup licate(); // get range, this will be blank (del not done)
selection.moveStart("chara cter",-1); // get the char you are going to delete
var selection = selection.text; // there got the deleted stuff
var whereat = document.selection.createR ange().dup licate();
whereat .moveStart("character",-1) ; // move to where this would go back
whereat .moveEnd("character",-1); // start and end now both at position
whereat .moveStart("textedit",-1); // move start to begining of textarea (sometimes this goes to begin of doc)
var whereat = whereat.text.length;
it will take lots of testing and be browser dependant, lots of weird things like to happen with the selections too, like textedit-1 takes you to the begining of the document so you need to embed a hidden span with funny chars so you can split the string to find the real value and such
oh, another sneekie way you could to it is
onbeforecut get selection
onkeydown get the char to the left of selection
then
insert some unique chars, split the whole textarea by the chars, count the chars in the first arry of the split, then remove chars :)
-w
here is the psudo of what you need to do
on before delete key or backspace key
var selection = document.selection.createR
selection.moveStart("chara
var selection = selection.text; // there got the deleted stuff
var whereat = document.selection.createR
whereat .moveStart("character",-1)
whereat .moveEnd("character",-1); // start and end now both at position
whereat .moveStart("textedit",-1);
var whereat = whereat.text.length;
it will take lots of testing and be browser dependant, lots of weird things like to happen with the selections too, like textedit-1 takes you to the begining of the document so you need to embed a hidden span with funny chars so you can split the string to find the real value and such
oh, another sneekie way you could to it is
onbeforecut get selection
onkeydown get the char to the left of selection
then
insert some unique chars, split the whole textarea by the chars, count the chars in the first arry of the split, then remove chars :)
-w
ASKER
Using a combination of the ideas you gave me and some ideas I read elsewhere to figure out the index position of the cursor in a textarea, I managed to succeed about 85%. The code below mostly works (although in some cases I don't know why it works), but it does NOT work appropriately when you try to delete (with the "delete" key) a single character. It works fine for these situations where you try to delete text: backspace a single character or backspace/delete/ctrl-x/ri ght-mouse- cut selected text. I trying playing around wth lots of combinations of changes, but none of them worked. (btw, I noticed that mozilla has a built in undo feature, but not IE.)
The code below basically takes the selected text, replaces it with a ~ (which should not be in the text in the first place), gets all of the text in the textarea (which now has the ~), finds the index of the ~, then reverts the text back the way it was, allowing any of the delete types to go forward. What appears to happen with the "delete" key where you are deleting a single character is that moveEnd() position of the textrange is incremented by 1 (to capture the character to be deleted, for undo purposes), the char to be deleted gets replaced by ~ (and is never changed back), and then the delete goes forward to the character to the right of the one you wanted deleted. For example, if you had:
ABC
and tried to delete the A, you would end up with:
~C
where the A was replaced by the ~ and the B deleted. I put a comment in that shows a line of code that will not delete the B, but still leaves the ~ in place.
Here is the relevant code.
<script language="javascript">
var versions = new Array();
function disableNonDelete()
{
var e = event;
if(e.keyCode == 8 || e.keyCode == 46 || (e.keyCode == 88 && e.ctrlKey))
{
var undoObject = new Object();
undoObject.text = "";
undoObject.start = 0;
var tr = document.selection.createR ange();
var trCopy = document.selection.createR ange().dup licate();
if(tr.text == "")
{
if(e.keyCode == 8)
{
tr.moveStart("character",- 1);
if(tr.text == "")
return true;
undoObject.text = tr.text;
}
else if(e.keyCode == 46)
{
tr.moveEnd("character",1);
if(tr.text == "")
return true;
undoObject.text = tr.text;
//tr.moveEnd("character",- 1);
}
else
return false;
}
else
undoObject.text = tr.text;
tr.text = "~";
undoObject.start = document.mda.delete_only.v alue.index Of("~");
versions[versions.length]= undoObject ;
tr = trCopy;
document.mda.undo_button.d isabled=fa lse;
return true;
}
else if (e.keyCode >= 37 && e.keyCode <=40)
return true;
else
return false;
}
// the function for handling the right mouse click-cut is similar to above and works fine
function undo()
{
if(versions.length > 0)
{
var undoObject = versions[versions.length-1 ];
versions.length=versions.l ength-1;
var text = document.mda.delete_only.v alue;
var before = text.substring(0,undoObjec t.start);
var after = text.substring(undoObject. start, text.length);
document.mda.delete_only.v alue = before + undoObject.text + after;
}
}
</script>
<form name="mda">
<textarea name="delete_only" cols="50" rows="10" wrap="soft"
onKeyDown="return disableNonDelete();"
onbeforepaste="event.retur nValue=fal se"
onpaste="event.returnValue =false"
oncut="return onCut();"
>
WHATEVER TEXT YOU WANT HERE
</textarea>
<p>
<button name="undo_button" onclick="if(versions.lengt h > 0) {undo(); if(versions.length == 0) {this.disabled=true;}}" disabled>Undo Last Change</button>
...
</form>
The code below basically takes the selected text, replaces it with a ~ (which should not be in the text in the first place), gets all of the text in the textarea (which now has the ~), finds the index of the ~, then reverts the text back the way it was, allowing any of the delete types to go forward. What appears to happen with the "delete" key where you are deleting a single character is that moveEnd() position of the textrange is incremented by 1 (to capture the character to be deleted, for undo purposes), the char to be deleted gets replaced by ~ (and is never changed back), and then the delete goes forward to the character to the right of the one you wanted deleted. For example, if you had:
ABC
and tried to delete the A, you would end up with:
~C
where the A was replaced by the ~ and the B deleted. I put a comment in that shows a line of code that will not delete the B, but still leaves the ~ in place.
Here is the relevant code.
<script language="javascript">
var versions = new Array();
function disableNonDelete()
{
var e = event;
if(e.keyCode == 8 || e.keyCode == 46 || (e.keyCode == 88 && e.ctrlKey))
{
var undoObject = new Object();
undoObject.text = "";
undoObject.start = 0;
var tr = document.selection.createR
var trCopy = document.selection.createR
if(tr.text == "")
{
if(e.keyCode == 8)
{
tr.moveStart("character",-
if(tr.text == "")
return true;
undoObject.text = tr.text;
}
else if(e.keyCode == 46)
{
tr.moveEnd("character",1);
if(tr.text == "")
return true;
undoObject.text = tr.text;
//tr.moveEnd("character",-
}
else
return false;
}
else
undoObject.text = tr.text;
tr.text = "~";
undoObject.start = document.mda.delete_only.v
versions[versions.length]=
tr = trCopy;
document.mda.undo_button.d
return true;
}
else if (e.keyCode >= 37 && e.keyCode <=40)
return true;
else
return false;
}
// the function for handling the right mouse click-cut is similar to above and works fine
function undo()
{
if(versions.length > 0)
{
var undoObject = versions[versions.length-1
versions.length=versions.l
var text = document.mda.delete_only.v
var before = text.substring(0,undoObjec
var after = text.substring(undoObject.
document.mda.delete_only.v
}
}
</script>
<form name="mda">
<textarea name="delete_only" cols="50" rows="10" wrap="soft"
onKeyDown="return disableNonDelete();"
onbeforepaste="event.retur
onpaste="event.returnValue
oncut="return onCut();"
>
WHATEVER TEXT YOU WANT HERE
</textarea>
<p>
<button name="undo_button" onclick="if(versions.lengt
...
</form>
No comment has been added lately, so it's time to clean up this TA.
I will leave the following recommendation for this question in the Cleanup topic area:
PAQ with points refunded
Please leave any comments here within the next four days.
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!
jAy
EE Cleanup Volunteer
I will leave the following recommendation for this question in the Cleanup topic area:
PAQ with points refunded
Please leave any comments here within the next four days.
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!
jAy
EE Cleanup Volunteer
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
onbeforepaste=event.return
onpaste=event.returnValue=
>HIHI</textarea>
no more pasten. killing the keys is probable your best bet too.
-w