Insert value into TEXTAREA where cursor was - javascript

I have a textarea and a div with values. When I click on a value I insert it into textarea. I need it to be inserted where my cursor was in textarea. Why do I say WAS? Because when I move it out and click on a value to insert, I assume it looses focus in the text area.
So, my question is, is there a way to "remember" the latest cursor position within textarea and then insert my values at that position?
Perhaps it could be a char number in a string?.. Currently I add it like this:
input.val( function( i, val ) { return val + " " + myInsert + " "; } );
Also I use jQuery, perhaps I could use it?

I've written a cross-browser jQuery plug-in for dealing with the caret/selection in textareas and text inputs called Rangy inputs (terrible name, I really should think of a better one). A combination of methods from this and the techniques in Edgar Villegas Alvarado's answer should do the trick, although in IE, the focusout event fires too late and you'll need to use the proprietary beforedeactivate event instead:
var $textBox = $("#foo");
function saveSelection(){
$textBox.data("lastSelection", $textBox.getSelection());
}
$textBox.focusout(saveSelection);
$textBox.bind("beforedeactivate", function() {
saveSelection();
$textBox.unbind("focusout");
});
When inserting text later, the following will insert text at the previous cursor position (or overwrite the previously selected text, if the user had selected any):
var selection = $textBox.data("lastSelection");
$textBox.focus();
$textBox.setSelection(selection.start, selection.end);
$textBox.replaceSelectedText("Some new text");
See jsFiddle example here: http://jsfiddle.net/rQXrJ/1/

Here is a working example of what you are trying to do check it out at:
http://jsfiddle.net/J5Z2n/1/
Hello there my good friend....
how do you do
the jQuery:
function insertAt (myField, myValue, startSel, endSel) {
if (startSel || startSel == '0') {
var startPos = startSel;
var endPos = endSel;
myField.val(myField.val().substring(0, startPos)+ myValue+ myField.val().substring(endPos, myField.val().length));
}
else {
myField.val() += myValue;
}
}
// calling the function
var targetBox = $('textarea#insert'),
startSel,
endSel;
targetBox.bind('focusout', function() {
//insertAtCursor(this, 'how do you do');
startSel = this.selectionStart;
endSel = this.selectionEnd;
});
$("#myvalue").click(function() {
var myValue = $(this).text();
insertAt(targetBox, myValue, startSel, endSel);
});
The original function was borrowed from this post
http://alexking.org/blog/2003/06/02/inserting-at-the-cursor-using-javascript
That should give you a solid head start I hope. Cheers

If the caret (the cursor) is somewhere in the text field, it registers in Javascript as an empty selection. That is, the selectionStart and selectionEnd attributes are the same. You can use those attributes to detect the position of the caret.

Apparently selectionStart and selectionEnd are quite useful here. Check this out:
http://www.scottklarr.com/topic/425/how-to-insert-text-into-a-textarea-where-the-cursor-is/

You can use the jQuery Caret plugin to get/set the cursor position .
Example usage:
var cursorPosition = $("#textbox").caret().start);
You could 'store' this position like this:
$("#textbox").focusout(function(){
var cursorPosition = $(this).caret().start);
$(this).data("lastCursorPos", cursorPosition);
});
To retrieve it (on your div click event), do this:
var lastCursorPosition = $("#textbox").data("lastCursorPos");
Hope this helps. Cheers

Related

Javascript: Cannot move back in HTML Input

I have a form where a user can enter a URL but I wanted to have it automatically remove spaces.
To do this I have the following jQuery function:
$('#URL').on('change keyup', function() {
var sanitized = $(this).val().replace(' ', '');
$(this).val(sanitized);
});
But the problem with this code is that you cannot use the arrow keys to move in the input. i.e. if I type in http://oogle.com and I want to use arrow keys to fix my spelling mistake, it will automatically keep the cursor on the very last character. On top of this, I cannot use Ctrl+A to select all the text.
Is there a way to have jQuery/Javascript automatically remove spaces while still being able to move around the input or select it all?
Here is my jsFiddle showing my issue.
Use keypress instead of keyup, so only characters are caught. In this case, discard a space if it is pressed.
Also check for a paste event, and use a regular expression to replace all spaces. Change the value within a timeout in order to capture the pasted value:
$('#URL')
.on('keypress', function(e) {
if(e.which === 32) return false;
})
.on('paste', function() {
$self= $(this);
setTimeout(function() {
$self.val($self.val().replace(/\s/g, ''));
});
});
Fiddle
Fiddle
You can:
Record the cursor position
Execute your original code
Then restore the cursor position
Update:
Make sure to change your .replace to .replace(/\ /g, "")
Update 2 (cursor position):
This fixes the cursor position when inserting spaces.
For example copy and pasting the following will now work with any cursor position:
341 10365
34 1 1 03 65
First you need to get the string to the left of the cursor:
var leftString = $(this).val().substring(0, start);
Then you need to count the spaces in that string:
var leftSpaces = (leftString.match(/ /g) || []).length;
Then subtract leftSpaces from the start and end variables.
Javascript
$('#URL').on('change keyup', function() {
// Store cursor position
var start = this.selectionStart;
var end = this.selectionEnd;
// Check for newly inserted spaces
var leftString = $(this).val().substring(0, start);
var leftSpaces = (leftString.match(/ /g) || []).length;
newStart = start - leftSpaces;
newEnd = end - leftSpaces;
// Original Code
var sanitized = $(this).val().replace(/\ /g, "");
$(this).val(sanitized);
// Place cursor in correct position
this.setSelectionRange(newStart, newEnd);
});
Another option would be to add a delay after the keyup event
$('#URL').on('change keyup', function() {
delay(function(){
var sanitized = $('#URL').val().replace(' ', '');
$('#URL').val(sanitized);
}, 1000 );
});
http://jsfiddle.net/xv2e9bLe/
The usual way to adjust user input in a form is done when user finish their input or edit. It's pretty awkward to adjust user input on the fly. So, the onBlur event usually the best event to catch and sanitize a form input.
Based on the answer from adriancarriger, I use the 'blur' event which catch the user input after the user finish the input and do something else.
$('#URL').on('blur', function() {
var start = this.selectionStart,
end = this.selectionEnd;
var sanitized = $(this).val().replace(/\ /g, "");
$(this).val(sanitized);
this.setSelectionRange(start, end);
});
This should work well. If you have a real form submit, you can also catch the user input at onSubmit event of the form as well. Note that onSubmit happens at the form object, not the input object.

Any way to prevent losing focus when clicking an input text out of tinymce container?

I've made this tinymce fiddle to show what I say.
Highlight text in the editor, then click on the input text, highlight in tinyMCE is lost (obviously).
Now, I know it's not easy since both, the inline editor and the input text are in the same document, thus, the focus is only one. But is there any tinymce way to get like an "unfocused" highlight (gray color) whenever I click in an input text?
I'm saying this because I have a customized color picker, this color picker has an input where you can type in the HEX value, when clicking OK it would execCommand a color change on the selected text, but it looks ugly because the highlight is lost.
I don't want to use an iframe, I know that by using the non-inline editor (iframe) is one of the solutions, but for a few reasons, i can't use an iframe text editor.
Any suggestion here? Thanks.
P.S: Out of topic, does any of you guys know why I can't access to tinymce object in the tinyMCE Fiddle ? looks like the tinyMCE global var was overwritten by the tinymce select dom element of the page itself. I can't execute a tinyMCE command lol.
Another solution:
http://fiddle.tinymce.com/sBeaab/5
P.S: Out of topic, does any of you guys know why I can't access to
tinymce object in the tinyMCE Fiddle ? looks like the tinyMCE global
var was overwritten by the tinymce select dom element of the page
itself. I can't execute a tinyMCE command lol.
Well, you can access the tinyMCE variable and even execute commands.
this line is wrong
var colorHex = document.getElementById("colorHex")
colorHex contains input element, not value.
var colorHex = document.getElementById("colorHex").value
now it works ( neolist couldn't load, so I removed it )
http://fiddle.tinymce.com/DBeaab/1
I had to do something similar recently.
First off, you can't really have two different elements "selected" simultaneously. So in order to accomplish this you're going to need to mimic the browser's built-in 'selected text highlight'. To do this, you're going to have to insert spans into the text to simulate highlighting, and then capture the mousedown and mouseup events.
Here's a fiddle from StackOverflow user "fullpipe" which illustrates the technique I used.
http://jsfiddle.net/fullpipe/DpP7w/light/
$(document).ready(function() {
var keylist = "abcdefghijklmnopqrstuvwxyz123456789";
function randWord(length) {
var temp = '';
for (var i=0; i < length; i++)
temp += keylist.charAt(Math.floor(Math.random()*keylist.length));
return temp;
}
for(var i = 0; i < 500; i++) {
var len = Math.round(Math.random() * 5 + 3);
document.body.innerHTML += '<span id="'+ i +'">' + randWord(len) + '</span> ';
}
var start = null;
var end = null;
$('body').on('mousedown', function(event) {
start = null;
end = null;
$('span.s').removeClass('s');
start = $(event.target);
start.addClass('s');
});
$('body').on('mouseup', function(event) {
end = $(event.target);
end.addClass('s');
if(start && end) {
var between = getAllBetween(start,end);
for(var i=0, len=between.length; i<len;i++)
between[i].addClass('s');
alert('You select ' + (len) + ' words');
}
});
});
function getAllBetween(firstEl,lastEl) {
var firstIdx = $('span').index($(firstEl));
var lastIdx = $('span').index($(lastEl));
if(lastIdx == firstIdx)
return [$(firstEl)];
if(lastIdx > firstIdx) {
var firstElement = $(firstEl);
var lastElement = $(lastEl);
} else {
var lastElement = $(firstEl);
var firstElement = $(lastEl);
}
var collection = new Array();
collection.push(firstElement);
firstElement.nextAll().each(function(){
var siblingID = $(this).attr("id");
if (siblingID != $(lastElement).attr("id")) {
collection.push($(this));
} else {
return false;
}
});
collection.push(lastElement);
return collection;
}
As you can see in the fiddle, the gibberish text in the right pane stays highlighted regardless of focus elsewhere on the page.
At that point, you're going to have to apply your color changes to all matching spans.

jQuery: scroll textarea to given position

I have a textarea with lots and lots of text:
<textarea cols="50" rows="10" id="txt">lots and lots of text goes here</textarea>
I want to scroll the textarea down, so the user can see 2000-th character. How can I do this using javasctipt/jQuery?
$('#txt').scrollToCharNo(2000); // something like this would be great
EDIT (my solution)
Well, I managed to make it work myself. The only way I found, is to create a DIV with the same font and width as the textarea, put a SPAN near the needed character and find the position of that span.
I am sure, that somebody might find my solution useful, so i'll paste it here:
jQuery.fn.scrollToText = function(search) {
// getting given textarea contents
var text = $(this).text();
// number of charecter we want to show
var charNo = text.indexOf(search);
// this SPAN will allow up to determine given charecter position
var anch = '<span id="anch"></span>';
// inserting it after the character into the text
text = text.substring(0, charNo) + anch + text.substring(charNo);
// creating a DIV that is an exact copy of textarea
var copyDiv = $('<div></div>')
.append(text.replace(/\n/g, '<br />')) // making newlines look the same
.css('width', $(this).attr('clientWidth')) // width without scrollbar
.css('font-size', $(this).css('font-size'))
.css('font-family', $(this).css('font-family'))
.css('padding', $(this).css('padding'));
// inserting new div after textarea - this is needed beacuse .position() wont work on invisible elements
copyDiv.insertAfter($(this));
// what is the position on SPAN relative to parent DIV?
var pos = copyDiv.find('SPAN#anch').offset().top - copyDiv.find('SPAN#anch').closest('DIV').offset().top;
// the text we are interested in should be at the middle of textearea when scrolling is done
pos = pos - Math.round($(this).attr('clientHeight') / 2);
// now, we know where to scroll!
$(this).scrollTop(pos);
// no need for DIV anymore
copyDiv.remove();
};
$(function (){
$('#scroll_button').click(
function() {
// scrolling to "FIND ME" using function written above
$('#txt').scrollToText('FIND ME');
return false;
}
);
});
Here is a demo (it works!): http://jsfiddle.net/KrVJP/
Since none of the answers actually solved the problem, I'll accept experimentX's one: thank you for putting an effort to help me, I appreciate it!
I am not sure if it will work. Please also check it here. It's seems working for 2000th, 1500th, and 1000th position.
EDIT confused font-size or line-height ???
$(function (){
var height = 2000/$('#txt').attr('cols');
$('#txt').scrollTop(height*13);
$('#txt').selectRange(2000,2000); //this is just to check
});
$.fn.selectRange = function(start, end) { //this is just to check
return this.each(function() {
if (this.setSelectionRange) {
this.focus();
this.setSelectionRange(start, end);
} else if (this.createTextRange) {
var range = this.createTextRange();
range.collapse(true);
range.moveEnd('character', end);
range.moveStart('character', start);
range.select();
}
});
};
UPDATE How about this one
var height = 1200/$('#txt').attr('cols');
var line_ht = $('#txt').css('line-height');
line_ht = line_ht.replace('px','');
height = height*line_ht;
You can use this jquery plugin

Keypress in jQuery: Press TAB inside TEXTAREA (when editing an existing text)

I want to insert TAB characters inside a TEXTAREA, like this:
<textarea>{KEYPRESS-INSERTS-TAB-HERE}Hello World</textarea>
I can insert before/after the existing TEXTAREA text - and I can insert / replace all text in the TEXTAREA - but have not yet been able to insert inside the existing TEXTAREA text (by the cursor) in a simple way.
$('textarea:input').live('keypress', function(e) {
if (e.keyCode == 9) {
e.preventDefault();
// Press TAB to append a string (keeps the original TEXTAREA text).
$(this).append("TAB TAB TAB AFTER TEXTAREA TEXT");
// Press TAB to append a string (keeps the original TEXTAREA text).
$(this).focus().prepend("TAB TAB TAB BEFORE TEXTAREA TEXT");
// Press TAB to replace a all text inside TEXTAREA.
$(this).val("INSERT INTO TEXTAREA / REPLACE EXISTING TEXT");
}
});
There is a "tabs in textarea" plug-in for jQuery ("Tabby") - but it's 254 code lines - I was hoping for just a few lines of code.
A few links that I studied: (again, I would prefer fewer code lines).
http://www.dynamicdrive.com/forums/showthread.php?t=34452
http://www.webdeveloper.com/forum/showthread.php?t=32317
http://pallieter.org/Projects/insertTab/
Please advise. Thanks.
I was creating a AJAX powered simple IDE for myself so I can rapidly test out PHP snippets.
I remember stumbling upon the same problem, here's how I solved it:
$('#input').keypress(function (e) {
if (e.keyCode == 9) {
var myValue = "\t";
var startPos = this.selectionStart;
var endPos = this.selectionEnd;
var scrollTop = this.scrollTop;
this.value = this.value.substring(0, startPos) + myValue + this.value.substring(endPos,this.value.length);
this.focus();
this.selectionStart = startPos + myValue.length;
this.selectionEnd = startPos + myValue.length;
this.scrollTop = scrollTop;
e.preventDefault();
}
});
#input is the ID of the textarea.
The code is not completely mine, I found it on Google somewhere.
I've only tested it on FF 3.5 and IE7. It does not work on IE7 sadly.
Unfortunately, manipulating the text inside textarea elements is not as simple as one might hope. The reason that Tabby is larger than those simple snippets is that it works better. It has better cross-browser compatibility and handles things like tabbing selections.
When minified, it's only about 5k. I'd suggest using it. You'll either have to discover and troubleshoot those same edge cases yourself anyway, or might not even know about them if users don't report them.
Yeah, dealing with input field selections across the different browsers is an annoyance, especially as in IE there are a few methods that look like they should work but actually don't. (Notably, combining using setEndPoint then measuring length, which looks OK until the selection starts or ends in newlines.)
Here's a couple of utility functions I use to deal with input selections. It returns the value of the input split into bits that are before, inside and after the selection (with the selection counting as an empty string at the input focus position if it's not a selection). This makes it fairly simply to replace and insert content at the point you want, whilst taking care of the IE CRLF problem.
(There may be a jQuery that does something like this, but I have yet to meet one.)
// getPartitionedValue: for an input/textarea, return the value text, split into
// an array of [before-selection, selection, after-selection] strings.
//
function getPartitionedValue(input) {
var value= input.value;
var start= input.value.length;
var end= start;
if (input.selectionStart!==undefined) {
start= input.selectionStart;
end= input.selectionEnd;
} else if (document.selection!==undefined) {
value= value.split('\r').join('');
start=end= value.length;
var range= document.selection.createRange();
if (range.parentElement()===input) {
var start= -range.moveStart('character', -10000000);
var end= -range.moveEnd('character', -10000000);
range.moveToElementText(input);
var error= -range.moveStart('character', -10000000);
start-= error;
end-= error;
}
}
return [
value.substring(0, start),
value.substring(start, end),
value.substring(end)
];
}
// setPartitionedValue: set the value text and selected region in an input/
// textarea.
//
function setPartitionedValue(input, value) {
var oldtop= input.scrollTop!==undefined? input.scrollTop : null;
input.value= value.join('');
input.focus();
var start= value[0].length;
var end= value[0].length+value[1].length;
if (input.selectionStart!==undefined) {
input.selectionStart= start;
input.selectionEnd= end;
if (oldtop!==null)
input.scrollTop= oldtop;
}
else if (document.selection!==undefined) {
var range= input.createTextRange();
range.collapse(true);
range.moveEnd('character', end);
range.moveStart('character', start);
range.select();
}
}
btw, see also:
http://aspalliance.com/346_Tabbing_in_the_TextArea

How do I insert a character at the caret with javascript?

I want to insert some special characters at the caret inside textboxes using javascript on a button. How can this be done?
The script needs to find the active textbox and insert the character at the caret in that textbox. The script also needs to work in IE and Firefox.
EDIT: It is also ok to insert the character "last" in the previously active textbox.
I think Jason Cohen is incorrect. The caret position is preserved when focus is lost.
[Edit: Added code for FireFox that I didn't have originally.]
[Edit: Added code to determine the most recent active text box.]
First, you can use each text box's onBlur event to set a variable to "this" so you always know the most recent active text box.
Then, there's an IE way to get the cursor position that also works in Opera, and an easier way in Firefox.
In IE the basic concept is to use the document.selection object and put some text into the selection. Then, using indexOf, you can get the position of the text you added.
In FireFox, there's a method called selectionStart that will give you the cursor position.
Once you have the cursor position, you overwrite the whole text.value with
text before the cursor position + the text you want to insert + the text after the cursor position
Here is an example with separate links for IE and FireFox. You can use you favorite browser detection method to figure out which code to run.
<html><head></head><body>
<script language="JavaScript">
<!--
var lasttext;
function doinsert_ie() {
var oldtext = lasttext.value;
var marker = "##MARKER##";
lasttext.focus();
var sel = document.selection.createRange();
sel.text = marker;
var tmptext = lasttext.value;
var curpos = tmptext.indexOf(marker);
pretext = oldtext.substring(0,curpos);
posttest = oldtext.substring(curpos,oldtext.length);
lasttext.value = pretext + "|" + posttest;
}
function doinsert_ff() {
var oldtext = lasttext.value;
var curpos = lasttext.selectionStart;
pretext = oldtext.substring(0,curpos);
posttest = oldtext.substring(curpos,oldtext.length);
lasttext.value = pretext + "|" + posttest;
}
-->
</script>
<form name="testform">
<input type="text" name="testtext1" onBlur="lasttext=this;">
<input type="text" name="testtext2" onBlur="lasttext=this;">
<input type="text" name="testtext3" onBlur="lasttext=this;">
</form>
Insert IE
<br>
Insert FF
</body></html>
This will also work with textareas. I don't know how to reposition the cursor so it stays at the insertion point.
In light of your update:
var inputs = document.getElementsByTagName('input');
var lastTextBox = null;
for(var i = 0; i < inputs.length; i++)
{
if(inputs[i].getAttribute('type') == 'text')
{
inputs[i].onfocus = function() {
lastTextBox = this;
}
}
}
var button = document.getElementById("YOURBUTTONID");
button.onclick = function() {
lastTextBox.value += 'PUTYOURTEXTHERE';
}
Note that if the user pushes a button, focus on the textbox will be lost and there will be no caret position!
loop over all you input fields...
finding the one that has focus..
then once you have your text area...
you should be able to do something like...
myTextArea.value = 'text to insert in the text area goes here';
I'm not sure if you can capture the caret position, but if you can, you can avoid Jason Cohen's concern by capturing the location (in relation to the string) using the text box's onblur event.
A butchered version of #bmb code in previous answer works well to reposition the cursor at end of inserted characters too:
var lasttext;
function doinsert_ie() {
var ttInsert = "bla";
lasttext.focus();
var sel = document.selection.createRange();
sel.text = ttInsert;
sel.select();
}
function doinsert_ff() {
var oldtext = lasttext.value;
var curposS = lasttext.selectionStart;
var curposF = lasttext.selectionEnd;
pretext = oldtext.substring(0,curposS);
posttest = oldtext.substring(curposF,oldtext.length);
var ttInsert='bla';
lasttext.value = pretext + ttInsert + posttest;
lasttext.selectionStart=curposS+ttInsert.length;
lasttext.selectionEnd=curposS+ttInsert.length;
}

Categories

Resources