How to insert text at the current caret position in a textarea - javascript

On a function call from an image, I am trying to insert the alt tag value from the image into the textarea at the position where the caret currently is.
This is the code that I currently have which inserts the alt tag value to the end of the text area.
$("#emoticons").children().children().click(function () {
var ch = $(this).attr("alt");
$("#txtPost").append(ch);
});
The 2 things I have been having a problem with is determining the position of the caret, and creating a new string with the value of the textarea before the carets positon + the code I'm inserting + the value of the textarea after the carets position.

i've currently got this extension in place:
$.fn.insertAtCaret = function(text) {
return this.each(function() {
if (document.selection && this.tagName == 'TEXTAREA') {
//IE textarea support
this.focus();
sel = document.selection.createRange();
sel.text = text;
this.focus();
} else if (this.selectionStart || this.selectionStart == '0') {
//MOZILLA/NETSCAPE support
startPos = this.selectionStart;
endPos = this.selectionEnd;
scrollTop = this.scrollTop;
this.value = this.value.substring(0, startPos) + text + this.value.substring(endPos, this.value.length);
this.focus();
this.selectionStart = startPos + text.length;
this.selectionEnd = startPos + text.length;
this.scrollTop = scrollTop;
} else {
// IE input[type=text] and other browsers
this.value += text;
this.focus();
this.value = this.value; // forces cursor to end
}
});
};
and you can use it like so:
$("#txtPost").insertAtCaret(ch);

Related

How to simulate the backspace key being pressed in JavaScript or jQuery

I have spent an hour reading a million different posts and can't get a winner.
Simply put. I have created an on-screen keyboard.
When a user presses a letter button, the letter is inserted at the carat in the input that has focus.
This all works fine and I know how to insert all the letters and numbers and spaces but I can't figure out how to backspace at the carat. I know how to take the last character off but that is not effective as I wish it to backspace at the carat.
I will insert the code to show how it is set up... The only part that does not work is the lines in the if ($(this).html() == 'BKSP') block.
PLEASE and THANKS!
function insertAtCursor(myField, myValue) {
//IE support
if (document.selection) {
myField.focus();
sel = document.selection.createRange();
sel.text = myValue;
}
//MOZILLA and others
else if (myField.selectionStart || myField.selectionStart == '0') {
var startPos = myField.selectionStart;
var endPos = myField.selectionEnd;
myField.value = myField.value.substring(0, startPos)
+ myValue
+ myField.value.substring(endPos, myField.value.length);
myField.selectionStart = startPos + myValue.length;
myField.selectionEnd = startPos + myValue.length;
} else {
myField.value += myValue;
}
}
$("#keyboard").on("pointerdown", function(e){
e.preventDefault();
});
$(".sm-kb-btn").on("pointerdown", function (e) {
if ($(this).html() == 'BKSP') {
var e = new Event("keydown");
e.key = "Backspace";
e.code = "Backspace";
document.getElementById("search-box-input").dispatchEvent(e);
}
else {
insertAtCursor(document.getElementById("search-box-input"), $(this).html());
}
})
The browser and javascript have limits when it comes to accessing to device hardware, for sercurity reasons. You can throw a keydown event, but it won't perform the same action as physically pressing a key.
If you're goal is just maintaining the caret position, you can set that using selection.setSelectionRange(caret_position, caret_position)
https://developer.mozilla.org/en-US/docs/Web/API/Selection
Set keyboard caret position in html textbox
Here's a demo:
let output = document.querySelector('input');
document.querySelector('.buttons').addEventListener('click', function(e){
if (e.target.nodeName === 'BUTTON') {
let caret_position = output.selectionStart || 0, //current caret position
character = e.target.textContent, //button / key pressed
new_caret_position = Math.max(0, caret_position + (character === 'BKSP' ? -1 : 1));
//if BKSP, move caret -1, else move caret +1. also make sure it's >= 0
if (character === 'BKSP'){ //remove character preceding current caret position
output.value = output.value.substr(0, new_caret_position) + output.value.substr(caret_position);
} else { //insert character at current character position
output.value = output.value.substr(0, caret_position) + character + output.value.substr(caret_position);
}
//reset the caret position after modifying output.value
output.setSelectionRange(new_caret_position, new_caret_position);
}
});
button{
height: 24px;
margin: 16px 4px;
}
<input>
<div class="buttons">
<button>Q</button>
<button>W</button>
<button>E</button>
<button>R</button>
<button>T</button>
<button>Y</button>
<button>BKSP</button>
</div>

vanilla javascript : intercept key on input and change key value

I want to intercept the keys typed in one input and change them to others.
For example, I want to simulate typing a 1 each time a key is pressed.
I was thinking to something like this :
//this example does not work, it will trigger an endless loop
Array.from(document.querySelectorAll('.onlyOne')).forEach(input =>
input.addEventListener('keydown', (event) => {
event.preventDefault();
event.srcElement.dispatchEvent(new KeyboardEvent('keydown', { 'key': 49 }));
});
}
);
I canot just add 1 whith event.target.value += 1;
cause when there is already text in the input and the cursor is not at the end of the text or the user has selected all text with the mouse , it would not act naturally if text is added at the end of input
Could you help me please?
By dispatching an event from within the event that causes the same event, you're creating an infinite loop that will cause a Range Error: Maximum call stack size exceeded.
Instead of the event, simply add a 1 to where the cursor is on each keydown.
Array.from(document.querySelectorAll('.onlyOne')).forEach(input =>
input.addEventListener('keydown', (event) => {
event.preventDefault();
event.target.insertAtCaret('1');
}));
HTMLInputElement.prototype.insertAtCaret = function (text) {
text = text || '';
if (document.selection) {
// IE
this.focus();
var sel = document.selection.createRange();
sel.text = text;
} else if (this.selectionStart || this.selectionStart === 0) {
// Others
var startPos = this.selectionStart;
var endPos = this.selectionEnd;
this.value = this.value.substring(0, startPos) +
text +
this.value.substring(endPos, this.value.length);
this.selectionStart = startPos + text.length;
this.selectionEnd = startPos + text.length;
} else {
this.value += text;
}
};
<input class='onlyOne' value="foo">
The HTMLInputElement.prototype.insertAtCaret is taken from this answer: https://stackoverflow.com/a/19961519/3993662
You can change that to a normal function if you don't want to extend the built in's prototype.

Insert text at current cursor position on dropdown list changed inside iframe

I am using a text editor provided by Microsoft ajax-toolkit.
It renders iframe on browser. I have added a dropdown in that editor and I want that when user changes the drop-down index the value should be added in the editor current cursor position.
I got a code on SO which gives me the current selected text inside editor is as follows
function getIframeSelectionText(iframe) {
var win = iframe.contentWindow;
var doc = iframe.contentDocument || win.document;
if (win.getSelection) {
return win.getSelection().toString();
} else if (doc.selection && doc.selection.createRange) {
return doc.selection.createRange().text;
}
}
But I want to add some text at the current position. The html is rendering as below
<td class="ajax__htmleditor_editor_editpanel"><div id="Editor1_ctl02" style="height:100%;width:100%;">
<iframe id="Editor1_ctl02_ctl00" name="Editor1_ctl02_ctl00" marginheight="0" marginwidth="0" frameborder="0" style="height:100%;width:100%;display:none;border-width:0px;">
</iframe><textarea id="Editor1_ctl02_ctl01" class="ajax__htmleditor_htmlpanel_default" style="height:100%;width:100%;display:none;"></textarea><iframe id="Editor1_ctl02_ctl02" name="Editor1_ctl02_ctl02" marginheight="0" marginwidth="0" frameborder="0" style="height:100%;width:100%;display:none;border-width:0px;">
</iframe>
</div></td>
I am trying as follow
$("#imgDropdown").change(function () {
//var iframeBody = $(window.Editor1_ctl02_ctl00.document.getElementsByTagName("body")[0]);
var iframe = document.getElementById("Editor1_ctl02_ctl00");
$("#Editor1_ctl02_ctl00").find("body").insertAtCaret("value");
//alert(getIframeSelectionText(iframe));
});
the function for inserting text is not working with iframe is as follow
$.fn.extend({
insertAtCaret: function (myValue) {
if (document.selection) {
this.focus();
sel = document.selection.createRange();
sel.text = myValue;
this.focus();
}
else if (this.selectionStart || this.selectionStart == '0') {
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;
} else {
this.value += myValue;
this.focus();
}
}
})
Easy, you just have to use.
$("#Editor1_ctl02_ctl00").contents().find('textarea').insertAtCaret('value');
Updated
Sorry, I thought the insertAtCaret function is working for you, you just needed to work inside iFrame. You can use this version of insertAtCaret:
jQuery.fn.extend({
insertAtCaret: function (html) {
var winObject = function (el){
var doc = el.ownerDocument;
return doc.defaultView || doc.parentWindow
};
return this.each(function (i) {
var sel, range, w = this;
w = winObject(w);
if (w.getSelection) {
// IE9 and non-IE
sel = w.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
// Range.createContextualFragment() would be useful here but is
// only relatively recently standardized and is not supported in
// some browsers (IE9, for one)
var el = w.document.createElement("div");
el.innerHTML = html;
var frag = w.document.createDocumentFragment(), node, lastNode;
while ((node = el.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if (w.document.selection && w.document.selection.type != "Control") {
// IE < 9
w.document.selection.createRange().pasteHTML(html);
}
}
)
}
});
and call it like:
$("#Editor1_ctl02_ctl00").contents().find('body').insertAtCaret($val);
Function adapted from here
Happy coding!
There seem to be a few issues here.
The Microsoft ajax-toolkit editor creates an iframe where the designMode property is turned on, and that's why it's editable, it has no value, and textNodes are added straight to the body, which makes it a little more difficult.
When you're selecting something from a dropdown, the focus is on the dropdown, and there is no caret position, as the focus is shifted away from the iFrame.
I'm assuming that the dropdown is in the top menubar for the editor or anywhere else that is outside the iFrame.
Also, the Microsoft ajax-toolkit editor has a recommended update, the HTMLEditorExtender.
The code you have to capture the caret position seems to be for a regular input / textarea, and you'd have to adapt that code to work with any Node inside an iframe that is in designMode, with it's own window and document etc.
Given the above considerations, this is what I came up with to do this
var frameID = 'Editor1_ctl02_ctl00',
selectID = 'imgDropdown',
iframe = document.getElementById(frameID),
iWin = iframe.contentWindow ? iframe.contentWindow : window.frames[frameID];
$(iWin).on('blur', function() {
$(iframe).data('range', getRange(iWin));
});
$('#' + selectID).on('change', function() {
var range = $(iframe).data('range');
addText(iWin, range, this.value);
});
function getRange(win) {
var sel, range, html;
if (win.getSelection) {
sel = win.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
}
} else if (win.document.selection && win.document.selection.createRange) {
range = win.document.selection.createRange();
}
return range;
}
function addText(win, range, text) {
if (win.getSelection) {
range.insertNode(win.document.createTextNode(text));
} else if (win.document.selection && win.document.selection.createRange) {
range.text = text;
}
}
FIDDLE

Get caret position in HTML input?

How do I get the index of the text caret in an input?
-> selectionStart
<!doctype html>
<html>
<head>
<meta charset = "utf-8">
<script type = "text/javascript">
window.addEventListener ("load", function () {
var input = document.getElementsByTagName ("input");
input[0].addEventListener ("keydown", function () {
alert ("Caret position: " + this.selectionStart);
// You can also set the caret: this.selectionStart = 2;
});
});
</script>
<title>Test</title>
</head>
<body>
<input type = "text">
</body>
</html>
The following will get you the start and end of the selection as character indices. It works for text inputs and textareas, and is slightly complicated because of IE's strange handling of line breaks.
function getInputSelection(el) {
var start = 0, end = 0, normalizedValue, range,
textInputRange, len, endRange;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart;
end = el.selectionEnd;
} else {
range = document.selection.createRange();
if (range && range.parentElement() == el) {
len = el.value.length;
normalizedValue = el.value.replace(/\r\n/g, "\n");
// Create a working TextRange that lives only in the input
textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end
// of the input, since moveStart/moveEnd doesn't return what we want
// in those cases
endRange = el.createTextRange();
endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
start = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len;
} else {
end = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1;
}
}
}
}
return {
start: start,
end: end
};
}
var textBox = document.getElementById("textBoxId");
textBox.focus();
alert( getInputSelection(textBox).start );
There is now a nice jQuery plugin for this: Caret plugin
Then you can just call $("#myTextBox").caret();
We had used something like this for an old javascript application, but I haven't tested it in a couple years:
function getCaretPos(input) {
// Internet Explorer Caret Position (TextArea)
if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
var bookmark = range.getBookmark();
var caret_pos = bookmark.charCodeAt(2) - 2;
} else {
// Firefox Caret Position (TextArea)
if (input.setSelectionRange)
var caret_pos = input.selectionStart;
}
return caret_pos;
}
Get coordinates (css: left:x , top:y) of the current caret position in order to position an element (e.g. show tooltip at caret position)
function getCaretCoordinates() {
let x = 0,
y = 0;
const isSupported = typeof window.getSelection !== "undefined";
if (isSupported) {
const selection = window.getSelection();
// Check if there is a selection (i.e. cursor in place)
if (selection.rangeCount !== 0) {
// Clone the range
const range = selection.getRangeAt(0).cloneRange();
// Collapse the range to the start, so there are not multiple chars selected
range.collapse(true);
// getCientRects returns all the positioning information we need
const rect = range.getClientRects()[0];
if (rect) {
x = rect.left; // since the caret is only 1px wide, left == right
y = rect.top; // top edge of the caret
}
}
}
return { x, y };
}
demo: https://codesandbox.io/s/caret-coordinates-index-contenteditable-9tq3o?from-embed
ref: https://javascript.plainenglish.io/how-to-find-the-caret-inside-a-contenteditable-element-955a5ad9bf81
Working example of getting cursor point in text box:
function textbox()
{
var ctl = document.getElementById('Javascript_example');
var startPos = ctl.selectionStart;
var endPos = ctl.selectionEnd;
alert(startPos + ", " + endPos);
}

How can i use javascript to insert text into a textarea?

I need to insert some text into a textarea at the place where the cursor is, how can i do this without jquery?
You may want to check the small code sample at:
Inserting at the cursor using JavaScript
Code from the above article:
function insertAtCursor(myField, myValue) {
if (document.selection) {
myField.focus();
sel = document.selection.createRange();
sel.text = myValue;
}
else if (myField.selectionStart || myField.selectionStart == '0') {
var startPos = myField.selectionStart;
var endPos = myField.selectionEnd;
myField.value = myField.value.substring(0, startPos)
+ myValue
+ myField.value.substring(endPos, myField.value.length);
} else {
myField.value += myValue;
}
}
// calling the function
insertAtCursor(document.getElementById('textarea_id'), 'sometext');
Please see this person's code here. This code uses the selection property of the document object to get the cursor position, and then builds a new string and stuffs it into the textarea. It also has a specialized routine for IE which has much more cumbersome logic for finding the cursor position.
Use an HTML title attribute? That will place tooltip text next to the cursor when it's over a particular element.
Or you could create a <div> with position: fixed, then position it at event.screenX, event.screenY:
<div id="tip" style="position: fixed; visibility: hidden;"></div>
<textarea onmousemove="position();" onmouseout="hide();"></texarea>
<script type="text/javascript">
function position() {
var d = document.getElementById('tip');
d.style.visibility = 'visible';
d.style.left = event.screenX + 'px';
d.style.top = event.screenX + 'py';
}
function hide() {
document.getElementById('tip').style.visibility = 'hidden';
}
</script>

Categories

Resources