This question already has answers here:
Inserting a text where cursor is using Javascript/jquery
(13 answers)
Closed 7 years ago.
I'm upgrading some Javascript which works on IE. However, I'm having some problems.
Heres the IE code:
var range = document.getElementById('text').contentWindow.window
.document.getElementById('Content').createTextRange();
var textObj = document.getElementById('text').contentWindow.window
.document.getElementById('Content');
var textFieldValue = theSmile;
if (range && textObj.CursorPos) {
var CursorPos = textObj.CursorPos;
CursorPos.text = CursorPos.text.charAt(CursorPos.text.length - 1)
== ' ' ?' ' + textFieldValue : textFieldValue;
} else {
textObj.value = textFieldValue;
}
I've tried replacing CreateTextRange with CreateRange for non-IE browsers, but this doesn't help. With code like this:
var range;
var textObj;
var iframeEl = document.getElementById('text');
if (iframeEl.contentDocument) { // DOM
range = iframeEl.contentDocument.getElementById('Content').createRange;
textObj= iframeEl.contentDocument.getElementById('Content');
} else if (iframeEl.contentWindow) { // IE win
range = iframeEl.contentWindow.document.getElementById('Content')
.createTextRange;
textObj= iframeEl.contentWindow.document.getElementById('Content');
}
Here's a function to insert text at the cursor in a textarea or text input, which is what it seems you have. It works in all major browsers:
function insertTextAtCursor(el, text) {
var val = el.value, endIndex, range, doc = el.ownerDocument;
if (typeof el.selectionStart == "number"
&& typeof el.selectionEnd == "number") {
endIndex = el.selectionEnd;
el.value = val.slice(0, endIndex) + text + val.slice(endIndex);
el.selectionStart = el.selectionEnd = endIndex + text.length;
} else if (doc.selection != "undefined" && doc.selection.createRange) {
el.focus();
range = doc.selection.createRange();
range.collapse(false);
range.text = text;
range.select();
}
}
You can use it as follows:
var iframeWin = document.getElementById('text').contentWindow;
var textObj = iframeWin.document.getElementById('Content');
insertTextAtCursor(textObj, "foo");
Related
I'm trying to replace "abc" to "d" for instance.
Example code replaces only one letter at cursor position but I need to replace multiple letters with the same idea.
$("#text").keypress(function(event) {
if (event.key === "a") {
event.preventDefault();
insertTextAtCursor(this, "b");
}
});
function insertTextAtCursor(el, text) {
var val = el.value, endIndex, range;
if (typeof el.selectionStart != "undefined" && typeof el.selectionEnd != "undefined") {
endIndex = el.selectionEnd;
el.value = val.slice(0, el.selectionStart) + text + val.slice(endIndex);
el.selectionStart = el.selectionEnd = endIndex + text.length;
} else if (typeof document.selection != "undefined" && typeof document.selection.createRange != "undefined") {
el.focus();
range = document.selection.createRange();
range.collapse(false);
range.text = text;
range.select();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="text"></textarea>
Any help ?
Quite simple, the issue here is that we need to be able to read the input's value, instead of the pressed keys.
//All the words that you need to place :)
let wanted = [
"abc",
"omg",
"x"
];
let replaceBy = "Test!";
$("#text").keyup((event) => {
let old = $("#text").val();
$("#text").val(old.replace(new RegExp(wanted.join('|'), "gi"), replaceBy));
});
There you got a testable fiddle, you can even replace multiple words/characters.
https://jsfiddle.net/fr24napw/24/
Hope it helps!
I want to enter a "/" when user enters MM(2 digit) so it will be like MM/YYYY.
I have done similar for credit card number input which insert a space after 4 digit on keypress.
let ccNumber = e.target.value.split(" ").join("");
if (ccNumber.length > 0) {
ccNumber = ccNumber.match(new RegExp('.{1,4}', 'g')).join(" ");
}
e.target.value = ccNumber;
Fiddle
This works with
Regular keyboard input
Copy/Cut/Paste
Selected text
Adding the /
Because you're programmatically adding the / character, you have to update the cursor position whenever that affects the new input value. This can be more than one character if the user is pasting something. Most of the code complexity revolves around this issue.
There are a lot of comments in the code explaining the various situations that come up because of the /.
Full Code
var date = document.getElementById('date');
date.addEventListener('keypress', updateInput);
date.addEventListener('change', updateInput);
date.addEventListener('paste', updateInput);
date.addEventListener('keydown', removeText);
date.addEventListener('cut', removeText);
function updateInput(event) {
event.preventDefault();
var string = getString(event);
var selectionStart = this.selectionStart;
var selectionEnd = this.selectionEnd;
var selectionLength = selectionEnd - selectionStart;
var sanitizedString = string.replace(/[^0-9]+/g, '');
// Do nothing if nothing is added after sanitization
if (sanitizedString.length === 0) {
return;
}
// Only paste numbers that will fit
var valLength = date.value.replace(/[^0-9]+/g, '').length;
var availableSpace = 6 - valLength + selectionLength;
// If `/` is selected it should not count as available space
if (selectionStart <= 2 && selectionEnd >= 3) {
availableSpace -= 1;
}
// Remove numbers that don't fit
if (sanitizedString.length > availableSpace) {
sanitizedString = sanitizedString.substring(0, availableSpace);
}
var newCursorPosition = selectionEnd + sanitizedString.length - selectionLength;
// Add one to cursor position if a `/` gets inserted
if (selectionStart <= 2 && newCursorPosition >= 2) {
newCursorPosition += 1;
}
// Previous input value before current cursor position
var valueStart = date.value.substring(0, this.selectionStart);
// Previous input value after current cursor position
var valueEnd = date.value.substring(this.selectionEnd, date.value.length);
var proposedValue = valueStart + sanitizedString + valueEnd;
// Remove anything that's not a number
var sanitized = proposedValue.replace(/[^0-9]+/g, '');
format(sanitized);
this.setSelectionRange(newCursorPosition, newCursorPosition);
}
function removeText(event) {
if (event.key === 'Backspace' || event.type === 'cut') {
event.preventDefault();
var selectionStart = this.selectionStart;
var selectionEnd = this.selectionEnd;
var selectionLength = selectionEnd - selectionStart;
// If pressing backspace with no selected text
if (selectionLength === 0 && event.type !== 'cut') {
selectionStart -= 1;
// Remove number from before `/` if attempting to delete `/`
if (selectionStart === 2) {
selectionStart -= 1;
}
}
var valueStart = date.value.substring(0, selectionStart);
var valueEnd = date.value.substring(selectionEnd, date.value.length);
// Account for added `/`
if (selectionStart === 2) {
selectionStart += 1;
}
var proposedValue = valueStart + valueEnd;
var sanitized = proposedValue.replace(/[^0-9]+/g, '');
format(sanitized);
this.setSelectionRange(selectionStart, selectionStart);
}
}
function getString(event) {
if (event.type === 'paste') {
var clipboardData = event.clipboardData || window.clipboardData;
return clipboardData.getData('Text');
} else {
return String.fromCharCode(event.which);
}
}
function format(sanitized) {
var newValue;
var month = sanitized.substring(0, 2);
if (sanitized.length < 2) {
newValue = month;
} else {
var year = sanitized.substring(2, 6);
newValue = month + '/' + year;
}
date.value = newValue;
}
<input id="date" type="text" maxlength="7">
Try:
var date = document.getElementById('date');
date.addEventListener('keypress', function (event) {
var char = String.fromCharCode(event.which),
offset = date.selectionStart;
console.log(offset)
if (/\d/.test(char) && offset < 7) {
if (offset === 2) {
offset += 1;
}
date.value = date.value.substr(0, offset) + char + date.value.substr(offset + 1);
date.selectionStart = date.selectionEnd = offset + 1;
}
if (!event.keyCode) {
event.preventDefault();
}
});
<input id="date" type="text" value="mm/yyyy" maxlength="6" size="6">
function keypress(elem) { // get Input
if (typeof elem == 'string') {
if (document.getElementById(elem)) elem = document.getElementById(elem);
if (typeof elem == 'string') elem = document.getElementsByName(elem).item(0);
}
const el = elem; //handle error if not found input
el.maxLength = 19;
el.addEventListener('keypress', function (e) {
const t = e.keyCode || e.which
if (t == 8 || (t > 47 && t < 58)) { // limit numeric characters and backspace
if (t != 8) {
if (el.value.length == 2) el.value += '/';
if (el.value.length == 5) el.value += '/';
if (el.value.length == 10) el.value += ' ';
if (el.value.length == 13) el.value += ':';
if (el.value.length == 16) el.value += ':';
}
} else {
e.preventDefault();
}
});}
I have a sentence defined as a list(a string? an array?- I'm not sure the correct term) that I want returned sequentially as any key is pressed. I'm trying to use .split. Here is the list and function:
var list1 = "This is a test".replace(/\s/g, "\xA0").split("")
function transformTypedChar(charStr) {
var position = $("#verse1").text().length;
if (position >= list1.length) return '';
else return list1[position];
}
Currently, the "T" from the beginning of the list is returned repeatedly, and the rest of the list is ignored. I'm calling the function as follows:
document.getElementById("verse1").onkeypress = function(evt) {
var val = this.value;
evt = evt || window.event;
// Ensure we only handle printable keys, excluding enter and space
var charCode = typeof evt.which == "number" ? evt.which : evt.keyCode;
if (charCode && charCode > 32) {
var keyChar = String.fromCharCode(charCode);
// Transform typed character
var mappedChar = transformTypedChar(keyChar);
var start, end;
if (typeof this.selectionStart == "number" && typeof this.selectionEnd ==
"number") {
// Non-IE browsers and IE 9
start = this.selectionStart;
end = this.selectionEnd;
this.value = val.slice(0, start) + mappedChar + val.slice(end);
// Move the caret
this.selectionStart = this.selectionEnd = start + 1;
} else if (document.selection && document.selection.createRange) {
// For IE up to version 8
var selectionRange = document.selection.createRange();
var textInputRange = this.createTextRange();
var precedingRange = this.createTextRange();
var bookmark = selectionRange.getBookmark();
textInputRange.moveToBookmark(bookmark);
precedingRange.setEndPoint("EndToStart", textInputRange);
start = precedingRange.text.length;
end = start + selectionRange.text.length;
this.value = val.slice(0, start) + mappedChar + val.slice(end);
start++;
// Move the caret
textInputRange = this.createTextRange();
textInputRange.collapse(true);
textInputRange.move("character", start - (this.value.slice(0,
start).split("\r\n").length - 1));
textInputRange.select();
}
return false;
}
};
How do I return the entire list, sequentially? In other words, on the first key event the "T" appears, on the second key event, the "h" appears and so on. I can achieve this in contenteditable divs, but I'm using input type: text fields as I want to autotab between.
The entire code is here: http://jsfiddle.net/NAC77/9/
Calling $(el).text() on input elements returns an empty string, you should be calling $(el).val(): http://jsfiddle.net/NAC77/10/
Is there a total solution for getting a caret position and/or selection inside every browser from different elements. I'm looking for a solution witch I can execute like
mGetCaretPosition(iControl) witch will return the caret position inside it`s element.
I have tried a lot of functions:
selection (window/document) [document=IE, window=Opera]
getSelection (window/document) [document=Firefox, document=Chrome, document=Safari]
selectionStart (input/textarea) [All]
craeteRange (selection)
createTextRange (selection)
Calling a method like document.selection.createRange().text doesn't return a caret position because it doesn't have a selection. When setting tRange.moveStart('character', -X) the X isn't a known value. When you use this inside a div and the caret is in the middle it takes the code before the div.
I have build this today. It`s a combination of youre response alex and al the other results inside google. I have tested it inside the browsers IE9, Chrome, Opera, Safari and Firefox on the PC and also on a HTC Sensation with Android with the default browser, Firefox, Chrome and Opera.
Only the Opera on the mobile device did have some troubles.
My solution:
// Control
var BSControl = function(iControl)
{
// Variable
var tControl = (typeof iControl == 'string' ? document.getElementById(iControl) : iControl);
// Get Caret
this.mGetCaret = function()
{
// Resultaat aanmaken
var tResult = -1;
// SelectionStart
// *) Input & Textarea
if(tResult == -1 && (tControl.selectionStart || tControl.selectionStart == '0'))
{
tResult = tControl.selectionStart;
}
// ContentWindow.GetSelection
// *) IFrame
if(tResult == -1 && (tControl.contentWindow && tControl.contentWindow.getSelection))
{
var tRange= tControl.contentWindow.getSelection().getRangeAt(0);
tResult = tRange.startOffset;
}
// GetSelection
// *) Div
if(tResult == -1 && (window.getSelection))
{
var tRange= window.getSelection().getRangeAt(0);
tResult = tRange.startOffset;
}
// Resultaat teruggeven
return tResult;
}
// Set Caret
this.mSetCaret = function(iPosition)
{
// SelectionStart
// *) Input & Textarea
if(tControl.selectionStart || tControl.selectionStart == '0')
{
tControl.selectionStart = iPosition;
tControl.selectionEnd = iPosition;
return;
}
// ContentWindow.GetSelection
// *) IFrame
if(tControl.contentWindow && tControl.contentWindow.getSelection)
{
var tRange = tControl.contentDocument.createRange();
tRange.setStart(tControl.contentDocument.body.firstChild, iPosition);
tRange.setEnd(tControl.contentDocument.body.firstChild, iPosition);
var tSelection = tControl.contentWindow.getSelection();
tSelection.removeAllRanges();
tSelection.addRange(tRange);
return;
}
// GetSelection
// *) Div
if(window.getSelection)
{
var tSelection = window.getSelection();
var tRange= tSelection.getRangeAt(0);
tRange.setStart(tControl.firstChild, iPosition);
tRange.setEnd(tControl.firstChild, iPosition);
tSelection.removeAllRanges();
tSelection.addRange(tRange);
return;
}
}
// Get Selection
this.mGetSelection = function()
{
// Resultaat aanmaken
var tResult = null;
// SelectionStart
// *) Input & Textarea
if(tResult == null && (tControl.selectionStart || tControl.selectionStart == '0'))
{
tResult = this.mGet().substring(tControl.selectionStart, tControl.selectionEnd);
}
// ContentWindow.GetSelection
// *) IFrame
if(tResult == null && (tControl.contentWindow && tControl.contentWindow.getSelection))
{
var tSelection = tControl.contentWindow.getSelection()
tResult = tSelection.toString();
}
// GetSelection
// *) Div
if(tResult == null && (window.getSelection))
{
var tSelection = window.getSelection()
tResult = tSelection.toString();
}
// Resultaat teruggeven
return tResult;
}
// Set Selection
this.mSetSelection = function(iFrom, iUntil)
{
// SelectionStart
// *) Input & Textarea
if(tControl.selectionStart || tControl.selectionStart == '0')
{
tControl.selectionStart = iFrom;
tControl.selectionEnd = iUntil;
return;
}
// ContentWindow.GetSelection
// *) IFrame
if(tControl.contentWindow && tControl.contentWindow.getSelection)
{
var tRange = tControl.contentDocument.createRange();
tRange.setStart(tControl.contentDocument.body.firstChild, iFrom);
tRange.setEnd(tControl.contentDocument.body.firstChild, iUntil);
var tSelection = tControl.contentWindow.getSelection();
tSelection.removeAllRanges();
tSelection.addRange(tRange);
return;
}
// GetSelection
// *) Div
if(window.getSelection)
{
var tSelection = window.getSelection();
var tRange= tSelection.getRangeAt(0);
tRange.setStart(tControl.firstChild, iFrom);
tRange.setEnd(tControl.firstChild, iUntil);
tSelection.removeAllRanges();
tSelection.addRange(tRange);
return;
}
}
// Set
this.mSet = function(iValue)
{
// Afhankelijk van aanwezige property waarde instellen
if('value' in tControl)
{
tControl.value = iValue;
}else if('innerText' in tControl)
{
tControl.innerText = iValue;
}else if('textContent' in tControl)
{
tControl.textContent = iValue;
}else if('innerHTML' in tControl)
{
tControl.innerHTML = iValue;
}
}
// Get
this.mGet = function()
{
// Resultaat aanmaken
var tResult = null;
// Afhankelijk van aanwezige property waarde instellen
if('value' in tControl)
{
tResult = tControl.value;
}else if('innerText' in tControl)
{
tResult = tControl.innerText;
}else if('textContent' in tControl)
{
tResult = tControl.textContent;
}else if('innerHTML' in tControl)
{
tResult = tControl.innerHTML;
}
// Resultaat teruggeven
return tResult;
}
}
in in Input field, if the user presses Backspace or Delete key, is there a way to get the deleted character.
I need to check it against a RegExp.
Assuming the input box has an id 'input'. Here is how with least amount of code you can find out the last character from the input box.
document.getElementById("input").onkeydown = function(evt) {
const t = evt.target;
if (evt.keyCode === 8) { // for backspace key
console.log(t.value[t.selectionStart - 1]);
} else if (evt.keyCode === 46) { // for delete key
console.log(t.value[t.selectionStart]);
}
};
<input id="input" />
The following will work in all major browsers for text <input> elements. It shouldn't be used for <textarea> elements because the getInputSelection function doesn't account for line breaks correctly in IE. See this answer for a (longer) function that will do this.
function getInputSelection(input) {
var start = 0, end = 0;
input.focus();
if ( typeof input.selectionStart == "number" &&
typeof input.selectionEnd == "number") {
start = input.selectionStart;
end = input.selectionEnd;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
if (range) {
var inputRange = input.createTextRange();
var workingRange = inputRange.duplicate();
var bookmark = range.getBookmark();
inputRange.moveToBookmark(bookmark);
workingRange.setEndPoint("EndToEnd", inputRange);
end = workingRange.text.length;
workingRange.setEndPoint("EndToStart", inputRange);
start = workingRange.text.length;
}
}
return {
start: start,
end: end,
length: end - start
};
}
document.getElementById("aTextBox").onkeydown = function(evt) {
evt = evt || window.event;
var keyCode = evt.keyCode;
var deleteKey = (keyCode == 46), backspaceKey = (keyCode == 8);
var sel, deletedText, val;
if (deleteKey || backspaceKey) {
val = this.value;
sel = getInputSelection(this);
if (sel.length) {
deletedText = val.slice(sel.start, sel.end);
} else {
deletedText = val.charAt(deleteKey ? sel.start : sel.start - 1);
}
alert("About to be deleted: " + deletedText);
}
};
No, there is no variable that stores the deleted char. Unless you have a history for Undo/Redo, but it would be difficult to get the information out of that component.
Easiest would be to compare the contents of the input field before and after delete/backspace have been pressed.
You could try something with the caret position:
function getCaretPosition(control){
var position = {};
if (control.selectionStart && control.selectionEnd){
position.start = control.selectionStart;
position.end = control.selectionEnd;
} else {
var range = document.selection.createRange();
position.start = (range.offsetLeft - 1) / 7;
position.end = position.start + (range.text.length);
}
position.length = position.end - position.start;
return position;
}
document.getElementById('test').onkeydown = function(e){
var selection = getCaretPosition(this);
var val = this.value;
if((e.keyCode==8 || e.keyCode==46) && selection.start!==selection.end){
alert(val.substr(selection.start, selection.length));
} else if(e.keyCode==8){
alert(val.substr(selection.start-1, 1));
} else if(e.keyCode==46){
alert(val.substr(selection.start, 1));
}
}
Tested on Chrome 6. See jsFiddle for an example