I am trying to insert text into a textarea at the cursor position or to replace a selected piece of text. The code works in that the text is inserted and if a selection of text is made it is replaced.
The problem is if the user inserts several line breaks first then tries to insert the text. Then instead of inserting the text at the point of the cursor it inserts it at the second line. So for example if the user inserts line breaks and ends on line 5 then clicks the input to insert the text, the text is inserted at line 2.
var holdFocus;
function updateFocus(x) {
holdFocus = x;
}
Triggered by:
<textarea onFocus="updateFocus(this)" cols="53" rows="10" name="entry" id="report" style="width: 98%;"></textarea>
Then the insertion of the text:
function InsertCodeInTextArea(text){
var tav = $(holdFocus).val(),
strPos = $(holdFocus)[0].selectionStart;
var endPos = $(holdFocus)[0].selectionEnd;
front = (tav).substring(0,strPos),
back = (tav).substring(endPos,tav.length);
var textValue = text.split("%lb%").join("\r\n");
$(holdFocus).val(front+ textValue + back);
}
Triggered by clicking on:
<input class="input" type="button" name="LODCTRRAPPA" value ="LODCTRRAPPA" onClick="InsertCodeInTextArea('Location: %lb%Onset: %lb%Duration: %lb%Course: %lb%Type of pain/symptom: %lb%')"/>
This problem seems to only be occurring in Chrome. Safari and FF are okay. Not tested IE. I am sure that this also wasn't happening until a few weeks ago.
Use vanilla JavaScript
From jQuery docs on val():
Note: At present, using .val() on elements strips carriage return characters from the browser-reported value. When this value is sent to the server via XHR, however, carriage returns are preserved (or added by browsers which do not include them in the raw value). A workaround for this issue can be achieved using a valHook as follows:$.valHooks.textarea = {
get: function( elem ) {
return elem.value.replace( /\r?\n/g, "\r\n" );
}
};
Something like the following should suffice:
var holdFocus;
function updateFocus(x) {
holdFocus = x;
}
function InsertCodeInTextArea(text){
var tav = holdFocus.value;
holdFocus.value = tav.substring(0, holdFocus.selectionStart) +
text.split("%lb%").join("\r\n") +
tav.substring(holdFocus.selectionEnd, tav.length);
}
<textarea autofocus onFocus="updateFocus(this)" cols="53" rows="10" name="entry" id="report" style="width: 98%;">foo
bar
baz</textarea>
<input class="input" type="button" name="LODCTRRAPPA" value ="LODCTRRAPPA" onClick="InsertCodeInTextArea('Location: %lb%Onset: %lb%Duration: %lb%Course: %lb%Type of pain/symptom: %lb%')"/>
Update
It's a bug! See productforums.google.com
... if you click out of the textarea and then click back in, and then hit the button the error is now gone and it works.
Tested my snippet, and it did indeed work after clicking out (losing focus) then clicked back in (regaining focus).
This would obviously cause loss of any selections made, and is impractical to ask users to do.
If I find a programmatic workaround, I'll update this answer.
Related
I am currently trying to create a syntax highlighter for Javascript and I currently facing the issue which I have found out is common with creating something like this which is setting the caret position to the end while the user types or edit contentEditable text.
I researched and found this and many other solutions here on SO but none works. It gets the position of the caret but never resets it so I am trying to find a workaround for this problem.
Below is the code I came up with.
html
<div id="editor" contentEditable="true" onkeyup="resetPosition(this)"></div>
<input type="text" onkeyup="resetPosition(this)" />
js
function getPos(e) {
// for contentedit field
if (e.isContentEditable) {
e.focus()
let _range = document.getSelection().getRangeAt(0)
let range = _range.cloneRange()
range.selectNodeContents(e)
range.setEnd(_range.endContainer, _range.endOffset)
return range.toString().length;
}
// for texterea/input element
return e.target.selectionStart
}
function setPos(pos, e) {
// for contentedit field
if (e.isContentEditable) {
e.focus()
document.getSelection().collapse(e, pos);
return
}
e.setSelectionRange(pos, pos)
}
function resetPosition(e) {
if(e.isContentEditable) {
let currentPosition = getPos(e);
e.innerHTML=e.innerHTML.replace(/[0-9]/g, "a");
setPos(currentPosition, e);
return;
}
e.value = e.value.replace(/[0-9]/g, "a");
setPos(currentPosition, e);
}
This works fine for text input but not for contentEditable divs.
When I type something like function, I get otincfun.
UPDATE: I was able to fix the setPos function by changing this line from document.getSelection().collapse(e, pos); to document.getSelection().collapse(e.firstChild, pos); but a new bug arose.
When I press ENTER Key, the caret goes back to the first line and first character. Please how do I fix?
Below is the fiddle link
https://jsfiddle.net/oketega/bfeh9nm5/35/
Thanks.
The Problems
document.getSelection().collapse(element, index) collapses the cursor to the child node that index points to, not the character index.
I was able to fix the setPos function by changing this line from document.getSelection().collapse(e, pos); to document.getSelection().collapse(e.firstChild, pos);
That will work if you are only replacing characters, but if you are creating a syntax highlighter, you will want to encase characters in span elements to style them. e.firstChild would then only set the position to an index within e's first child, excluding latter span's
Another thing to consider is that you may want to autocomplete the certain chars. The caret position before you manipulate the text may not be the same as after you do so.
The Solution
I recommend creating a <span id="caret-position"></span> element to track where the caret is.
It would work like this:
function textChanged(element) {
// 1
const text = setCursorMarker(element.innerText, element);
// 2
const html = manipulate(text);
element.innerHTML = html;
// 3
const index = findCursorIndex(element);
document.getSelection().collapse(element, index)
}
Every time the user types, you can get the current caret position and slip in the #caret-position element in there.
Overwrite the existing html with the syntax highlighted text
Find out where #caret-position is and put the caret there.
Note: The recommended way to listen for when the user types in the content-editable element is with the oninput listener, not onkeyup. It is possible to insert many characters by holding down a key.
Example
There is a working js fiddle here: https://jsfiddle.net/Vehmloewff/0j8hzevm/132/
Known Issue: After you hit Enter twice, it looses track of where the caret is supposed to be. I am not quite sure why it does that.
I have a TEXTAREA control (ID="taCode"), maxlength=400, that is pre-filled with 400 whitespaces.
I use Javascript to force insert-mode (replace) when entering text into textarea:
var input = document.getElementById("taCode");
input.addEventListener('keypress', function(){
var s = this.selectionStart;
var e = this.selectionEnd;
this.value = this.value.substr(0, s) + this.value.substr(e + 1);
if (this.value.length < 399)
{
this.value += new Array(399-this.value.length).join(' ');
}
this.selectionEnd = this.selectionStart = s;
}, false);
HTML:
<TEXTAREA ID="taCode" COLS="80" ROWS="5" MAXLENGTH="400" style="overflow:hidden"> </TEXTAREA>
Everything works fine using a desktop and mouse, but trying to place the cursor inside the TEXTAREA using a mobile (Iphone) fails (EDIT: Placing cursor on first row works sometimes) and nothing can be typed into the field, except for on the first line. Any idea of how to get it to work for mobiles for all lines?
Test: http://artificial.se/ta.html
Your first line in iPhone is completely populated because of spaces.So, when you put your cursor there and try to type, it moves on to next line so as to accommodate all the spaces which are prepopulated by you.If you clear those spaces and try to type on iPhone or reduce the number of spaces and try it.It would definitely work.
If not then add word-break:break-word property to your textarea element to make it work.
Try something like:
area.addEventListener("input", function(e) {
const caretIndex = area.selectionStart;
const content = area.value;
const newContent = str.slice(0, caretIndex) + e.key + str.slice(4);
area.value = newContent;
});
This will likely need a little work still. Some combination of input/change/keydown should work. I'd also suggest making the number of spaces one less than you need, and later fill the end with an extra space when you are using the entered value.
may be you can use the keydown event for this (sorry i just want to comment but my reputation is low)
I suggest that if you want spaces appended you can get the remaining spaces appended in form submit, working with js in this can be avoided and will be better after user has filled the input field.
I was able to get the highlighted text out of a textarea by recording onselect and storing the beginning and end each time. Then, when I click a button, I build the substring myself. Isn't there a simpler way of simply querying the selection?
I was kind of expecting that there would be methods in html5 dom for all these things, something like:
textarea.getSelectedStart()
textarea.getSelectedEnd();
textArea.setSelected(start,end);
Also, is there a way of programmatically deselecting text in a textarea?
I am putting in code based on the first solution below. This sort of works, but has a weird problem:
<script language=javascript>
function replaceCLOZE(code, questionType) {
var v = code.value;
var s = code.selectionStart;
var e = code.selectionEnd;
var t = v.substr(s, e-s);
var rep = "{:" + questionType + ":="+t+"}";
code.value = v.substr(0,s) + rep + v.substr(e,v.length-e+1);
}
function shortAnswer(code) {
replaceCLOZE(code, "SA");
}
function shortAnswerCaseSensitive(code) {
replaceCLOZE(code, "SAC");
}
function multipleChoice(code) {
replaceCLOZE(code, "MC");
}
The text area does in fact have attributes code.selectionStart and code.selectionEnd. But the code above, which now works, sets the highlighted text on the screen to be the first word in the textarea. Mind you, the selectionStart is still correct, but what is actually highlighted in Firefox is wrong.
In Chrome it works fine. Maybe this is just a bug in firefox or is there something else which should be done to properly update the textarea visually?
Following is simple way to get selected text of textarea of html. Still not clear what you want as following method simply will give you selected text in alert.
<html><head>
<script>
function alertme(){
var textarea = document.getElementById("textArea");
var selection = (textarea.value).substring(textarea.selectionStart,textarea.selectionEnd);
alert (selection);
}
</script>
</head>
<body>
<p><textarea class="noscrollbars" id="textArea"></textarea></p>
<button onclick="alertme()">Click me</button>
</body></html>
When you select text, the button will alert you what you have selected. selectionStart gives you starting point and selectionEnd gives you end point, while substring needs three arguments string, starting point and ending point.
I am developing a keyboard plugin. Which requires that if the user clicks on the enter key, then it must add the newline in that textarea.
I have tried adding other characters, such as a b c etc. But in case of newline I know a newline in js is added using
\n
But, when I try to append this to the textarea it just adds that to the textarea's text. While I want it to start a newline. How can I get the new line?
Here is the code that runs with each click:
$('#vboard li').click(function () {
CurVal = $(LastFocused).val();
$(LastFocused).val(CurVal + $(this).attr('id'));
$(LastFocused).focus();
});
The above code executes and appends the current button's value to the element. When I click on enter key, it gives me output as value + '\n' for example:
If the value was afzaal, then after the click it would change to afzaal\n instead of starting a new line.
How would I get the newline in textarea?
You need to add a new new dynamicaly, right?
Give this a try and see if this is what you wanted.
$('textarea').html($('textarea').val()+'
Something');
I'm setting html of textarea instead of its val
It seems even this works fine
$('textarea').val($('textarea').val()+'\nSomething');
this one should work with vanila Javascript:
var newMessage="please add me to the new line";
const textarea = document.getElementById('messages');
element.value += "\n"+ newMessage;
try somthing like
<textarea cols='60' rows='8'>This is my first line.
This is my second line </textarea>
using
and
will let you on new line without displaying in textarea
Firefox inserts a <br /> tag on press enter whereas the other browsers are adding either a <p> or <div>. I know that chrome and safari are inserting the same tag of the firstchild of the contentEditable div. So do Firefox, however, if the first tag's innerHTML is empty firefox is just ignoring the tag and creating a new line by pushing the default node in the second line and writes directly inside the editor instead of inside a child node. So basically, I want Firefox to write inside the given tag and continue to insert that kind of node on each press on enter. How can it be done? Any suggestions?
I've found the solution :) In order to make this work, you've to give an id to the caret's parent element. Then you can use the following code. Please note that I get the browsernName from c# and put it into a hidden field. That's why I equaled it to "firefox". The following code is tested with FF 3.6 and it works perfectly. The only thing is that you'll have to check the caret's parent element and if it is not equal to the current row's id, then you'll have to place the caret inside the current row by using a selection function. Also, perform this code on the keyup event and make sure that if you perform some other codes on the keyup event, put this code at the end of it! Anways, enjoy :)
// The code works good in the following cases:
// 1) If there is text inside the editor and the user selects the whole text
// and replace it with a single character, the <p> tag will be placed and the
// text will place inside the p tag
// 2) If the user selects the whole code and deletes it and begins to type again
// 3) If the user types normally and press enter
// NB: Please note me if you find any bug
if (browserName == "firefox") {
//remove all br tags
var brs = txteditor.getElementsByTagName("br");
for (var i = 0; i < brs.length; i++) { brs[i].parentNode.removeChild(brs[i]); }
//check whether there is a p tag inside
var para = txteditor.getElementsByTagName("p");
if (para.length == 0) {
var inner = txteditor.innerHTML.replace(/^\s+|\s+$/g, '');
var str = (inner == "") ? "" : txteditor.innerHTML;
var nText = "<p id=\"" + cRow + "\">" + str + "</p>";
// in order to prevent a dublicate row clear inside the text editor
txteditor.innerHTML = "";
document.execCommand('insertHTML', false, nText);
} else {
// always make sure that the current row's innerHTML is never empty
if (document.getElementById(cRow).innerHTML == "")
document.getElementById(cRow).innerHTML = "";
}
}
Try inserting a <p></p> inside your element. Then almost certainly every newline will be a new paragraph.
If you change the contenteditable element to be a <span> instead of a <div> the new line will be made with a <br>. I haven't tested this with other elements, but it would be interesting to see how different elements would behave.
A <span> element can be styled with display: block to make it look like a <div> element.