im trying to make a simple texteditor. its work fine, but i want to add the text on the selected text at textarea.
in my code, when i click bold button it will show [b][/b] with cursor focused inside b tags, and when i click the button again, it will show inside b tags again. (this is what i want dont mind this code)
function typeInTextarea(el, newText)
{
var start = el.prop("selectionStart")
var end = el.prop("selectionEnd")
var text = el.val()
var before = text.substring(0, start)
var after = text.substring(end, text.length)
el.val(before + newText + after)
el[0].selectionStart = el[0].selectionEnd = start - 4 + newText.length
el.focus()
return false
}
$("#button-bold").on("click", function()
{
typeInTextarea($("#textareapost"), "[b][/b]")
return false
});
what im looking right now is, when I select "abcd" then i click the button, it will show [b]abcd[/b], i success with this code:
function typeInTextarea(el, newText)
{
var start = el.prop("selectionStart")
var end = el.prop("selectionEnd")
var text = el.val()
var before = text.substring(0, start)
var after = text.substring(end, text.length)
el.val(before + newText.substring(0,3) + after + newText.substring(3,7)) //i making change in this one
el[0].selectionStart = el[0].selectionEnd = newText.length + end
el.focus()
return false
}
but when i didnt select the "abcd" text, it will show [b]abcd[/b][b][abcd/b]. just like copying the value of the text.
what im asking is, how to add value on selected text (not replace it)
and making if function when text is selected will add [b]abcd[/b] else will add [b] [/b] while the [b]abcd[/b] still there.
basicly it will be like stackoverflow editor, but without live view. thanks for advance, hope I found the answer. been looking this for a weeks.
found 1 answer
JQUERY Set Textarea Cursor to End of Text
and modify to
var b = { pos: 3, txt: function(s){ return '[b]' + s +'[/b]'; }}
$.fn.selectRange = function(start, end) {
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();
}
});
};
$('#this-b').on('click',function(e){
var cc = $('#this-txta')[0].selectionStart;
var str = $('#this-txta').val();
var en = $('#this-txta')[0].selectionEnd;
var sstr = str.substring(cc,en);
$('#this-txta').val( str.substring(0, cc) + b.txt(sstr) + str.substring(en) ).focus();
var pst = cc + b.pos
var pen = en + b.pos
$('#this-txta').selectRange(pst,pen);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="this-b"><b>B</b></button>
<br/>
<textarea id="this-txta">xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</textarea>
Sceditor has similar functionality and is open source.
Check how it was done here
http://www.sceditor.com/
Related
I'd like to get the word after # depending on the current writing position of a textarea. More precisely:
if the current cursor position is on any letter of #<user>, the answer should be <user>
if the current cursor position is on any other word, the answer should be empty ''
I'm struggling with this, but don't find any "nice" way to do it.
$('#hey').on('click', function() { alert(); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<textarea id="chat">hello #user it's me this is a long text, here is another #username cheers!</textarea>
<span id="hey">CLICK ME</span>
Having updated the code from the assumed duplicate Get current word on caret position, the result is as follows
function getCaretPosition(ctrl) {
var start, end;
if (ctrl.setSelectionRange) {
start = ctrl.selectionStart;
end = ctrl.selectionEnd;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
start = 0 - range.duplicate().moveStart('character', -100000);
end = start + range.text.length;
}
return {
start: start,
end: end
}
}
$("textarea").on("click keyup", function () {
var caret = getCaretPosition(this);
var endPos = this.value.indexOf(' ',caret.end);
if (endPos ==-1) endPos = this.value.length;
var result = /\S+$/.exec(this.value.slice(0, endPos));
var lastWord = result ? result[0] : null;
if (lastWord) lastWord = lastWord.replace(/['";:,.\/?\\-]$/, ''); // remove punctuation
$("#atID").html((lastWord && lastWord.indexOf("#") == 0)?lastWord:"");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<textarea>Follow me on twitter #mplungjan if you want</textarea><br/>
<span id="atID"></span>
My biggest problem is that after it's replaced, the cursor defaults to the end of the textarea. That's no issue if I'm typing, but if I'm going back and editing, it's really annoying. Here's what I tried (the id of the textarea is "area")
var el = e.area;
position = el.selectionStart; // Capture initial position
el.value = el.value.replace('\u0418\u0410', '\u042F');
el.selectionEnd = position; // Set the cursor back to the initial position.
You can try the following code snippet. In its current form, it replaces == with +, but it allows to replace any string with another one, shorter or longer.
In order to maintain the cursor position, you have to save and restore the selectionStart and the selectionEnd. An offset is calculated to account for the difference in length between the two strings, and the number of occurrences before the cursor.
The use of setTimeout ensures that the newly typed character has been inserted in the text before doing the processing.
var area = document.getElementById("area");
var getCount = function (str, search) {
return str.split(search).length - 1;
};
var replaceText = function (search, replaceWith) {
if (area.value.indexOf(search) >= 0) {
var start = area.selectionStart;
var end = area.selectionEnd;
var textBefore = area.value.substr(0, end);
var lengthDiff = (replaceWith.length - search.length) * getCount(textBefore, search);
area.value = area.value.replace(search, replaceWith);
area.selectionStart = start + lengthDiff;
area.selectionEnd = end + lengthDiff;
}
};
area.addEventListener("keypress", function (e) {
setTimeout(function () {
replaceText("==", "+");
}, 0)
});
<textarea id="area" cols="40" rows="8"></textarea>
I am trying to retrieve/find the start point and end point of selection in textarea.
Here is my code which work fine in Mozilla and chrome, but it is not working in Internet Explorer 9:
<script type="txt/javascript">
function update(o) {
var t = o.value, s = getSelectionStart(o), e = getSelectionEnd(o);
alert("start: " + s + " End: " + e);
}
function getSelectionStart(o) {
if (o.createTextRange) {
var r = document.selection.createRange().duplicate()
rse = r.text.length;
r.moveEnd('character', o.value.length)
if (r.text == '')
return o.value.length
return o.value.lastIndexOf(r.text)
}
else
return o.selectionStart
}
function getSelectionEnd(o) {
if (o.createTextRange) {
var r = document.selection.createRang;
e().duplicate()
r.moveStart('character', -o.value.length)
return r.text.length
}
else
return o.selectionEnd
}
</script>
<textarea id ="text" rows=10 cols="50" onselect="update(this);"></textarea>
When I test this code in Mozilla and Chrome, it gives me correct answer, but when I run this code in Internet Explorer 9, it shows -1 for start and any value for end.
I want to just find out the start and end point/index of the selected text of the textarea. Actually, the above code works fine for a textbox in all browsers, but not with textarea.
How can I fix it?
Use the code below or check this fiddle
function getTextSelection(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;
}
}
}
}
alert("start :" + start + " End :" + end);
}
While the original answer may have helped the OP in 2012, things have changed, and this has now become simpler, at least in modern browsers.
You can use the JavaScript selectionStart and selectionEnd attributes of the textarea.
Basic Usage
These are both standard attributes that will work in today's major browsers (Chrome, Safari, Firefox, Opera, Edge, and IE).
For example:
console.log(
document.getElementById("text").selectionStart,
document.getElementById("text").selectionEnd
)
will show both the start and end point of the selection in the textarea with the ID text.
Boundary Cases
If there is no item selected in the textarea, then both the start and end attributes will return the last position of the caret. If the textarea has not received focus yet, the attributes will both return a value of 0.
Changing the Selection
By setting these attributes to new values, you will adjust the active text selection. Thus, you can also use this to select text in a textarea.
Checking if a selection is in place
You can check if there is currently a selection by checking if the values are both different, i.e. if
document.getElementById("text").selectionStart != document.getElementById("text").selectionEnd
is true, then there is a text selection.
Demo
const textarea = document.getElementById("text");
const selStart = document.getElementById("selStart");
const selEnd = document.getElementById("selEnd");
// should maybe also look at other events, e.g. "keydown", "select", etc
textarea.addEventListener("mousemove", () => {
selStart.innerText = textarea.selectionStart;
selEnd.innerText = textarea.selectionEnd;
});
<textarea id="text">Some text here</textarea>
<p>Selection Start: <span id="selStart">0</span></p>
<p>Selection End: <span id="selEnd">0</span></p>
References
Live Demo (JSFiddle)
Documentation at MDN
Documentation at MSDN
I would like to find out and keep track of the 'line number' (rows) of the cursor in a textarea. (The 'bigger picture' is to parse the text on the line every time a new line is created/modified/selected, if of course the text was not pasted in. This saves parsing the whole text un-necessarily at set intervals.)
There are a couple of posts on StackOverflow however none of them specifically answer my question, most questions are for cursor position in pixels or displaying lines numbers besides the textarea.
My attempt is below, it works fine when starting at line 1 and not leaving the textarea. It fails when clicking out of the textarea and back onto it on a different line. It also fails when pasting text into it because the starting line is not 1.
My JavaScript knowledge is pretty limited.
<html>
<head>
<title>DEVBug</title>
<script type="text/javascript">
var total_lines = 1; // total lines
var current_line = 1; // current line
var old_line_count;
// main editor function
function code(e) {
// declare some needed vars
var keypress_code = e.keyCode; // key press
var editor = document.getElementById('editor'); // the editor textarea
var source_code = editor.value; // contents of the editor
// work out how many lines we have used in total
var lines = source_code.split("\n");
var total_lines = lines.length;
// do stuff on key presses
if (keypress_code == '13') { // Enter
current_line += 1;
} else if (keypress_code == '8') { // Backspace
if (old_line_count > total_lines) { current_line -= 1; }
} else if (keypress_code == '38') { // Up
if (total_lines > 1 && current_line > 1) { current_line -= 1; }
} else if (keypress_code == '40') { // Down
if (total_lines > 1 && current_line < total_lines) { current_line += 1; }
} else {
//document.getElementById('keycodes').innerHTML += keypress_code;
}
// for some reason chrome doesn't enter a newline char on enter
// you have to press enter and then an additional key for \n to appear
// making the total_lines counter lag.
if (total_lines < current_line) { total_lines += 1 };
// putput the data
document.getElementById('total_lines').innerHTML = "Total lines: " + total_lines;
document.getElementById('current_line').innerHTML = "Current line: " + current_line;
// save the old line count for comparison on next run
old_line_count = total_lines;
}
</script>
</head>
<body>
<textarea id="editor" rows="30" cols="100" value="" onkeydown="code(event)"></textarea>
<div id="total_lines"></div>
<div id="current_line"></div>
</body>
</html>
You would want to use selectionStart to do this.
<textarea onkeyup="getLineNumber(this, document.getElementById('lineNo'));" onmouseup="this.onkeyup();"></textarea>
<div id="lineNo"></div>
<script>
function getLineNumber(textarea, indicator) {
indicator.innerHTML = textarea.value.substr(0, textarea.selectionStart).split("\n").length;
}
</script>
This works when you change the cursor position using the mouse as well.
This is tough because of word wrap. It's a very easy thing to count the number of line breaks present, but what happens when the new row is because of word wrap? To solve this problem, it's useful to create a mirror (credit: github.com/jevin). Here's the idea:
Create a mirror of the textarea
Send the content from the beginning of the textarea to the cursor to the mirror
Use the height of the mirror to extract the current row
On JSFiddle
jQuery.fn.trackRows = function() {
return this.each(function() {
var ininitalHeight, currentRow, firstIteration = true;
var createMirror = function(textarea) {
jQuery(textarea).after('<div class="autogrow-textarea-mirror"></div>');
return jQuery(textarea).next('.autogrow-textarea-mirror')[0];
}
var sendContentToMirror = function (textarea) {
mirror.innerHTML = String(textarea.value.substring(0,textarea.selectionStart-1)).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br />') + '.<br/>.';
calculateRowNumber();
}
var growTextarea = function () {
sendContentToMirror(this);
}
var calculateRowNumber = function () {
if(firstIteration){
ininitalHeight = $(mirror).height();
currentHeight = ininitalHeight;
firstIteration = false;
} else {
currentHeight = $(mirror).height();
}
// Assume that textarea.rows = 2 initially
currentRow = currentHeight/(ininitalHeight/2) - 1;
//remove tracker in production
$('.tracker').html('Current row: ' + currentRow);
}
// Create a mirror
var mirror = createMirror(this);
// Style the mirror
mirror.style.display = 'none';
mirror.style.wordWrap = 'break-word';
mirror.style.whiteSpace = 'normal';
mirror.style.padding = jQuery(this).css('padding');
mirror.style.width = jQuery(this).css('width');
mirror.style.fontFamily = jQuery(this).css('font-family');
mirror.style.fontSize = jQuery(this).css('font-size');
mirror.style.lineHeight = jQuery(this).css('line-height');
// Style the textarea
this.style.overflow = "hidden";
this.style.minHeight = this.rows+"em";
var ininitalHeight = $(mirror).height();
// Bind the textarea's event
this.onkeyup = growTextarea;
// Fire the event for text already present
// sendContentToMirror(this);
});
};
$(function(){
$('textarea').trackRows();
});
This worked for me:
function getLineNumber(textarea) {
return textarea.value.substr(0, textarea.selectionStart) // get the substring of the textarea's value up to the cursor position
.split("\n") // split on explicit line breaks
.map((line) => 1 + Math.floor(line.length / textarea.cols)) // count the number of line wraps for each split and add 1 for the explicit line break
.reduce((a, b) => a + b, 0); // add all of these together
};
Inspired by colab's answer as a starting point, this includes the number of word wraps without having to introduce a mirror (as in bradbarbin's answer).
The trick is simply counting how many times the number of columns textarea.cols can divide the length of each segment between explicit line breaks \n.
Note: this starts counting at 1.
I have a function that displays a countdown next to a text field for the number of characters in the field (think twitter)
<script language="javascript" type="text/javascript">
function countDown(control, maxLen, counter, typeName) {
var len = control.value.length;
var txt = control.value;
var span = document.getElementById(counter);
span.style.display = '';
span.innerHTML = (maxLen - len);
if (len >= (maxLen - 10)) {
span.style.color = 'red';
} else {
span.style.color = '';
}
}
</script>
And the next field down takes a comma separated value. Example:
tomato, apple, orange, pear
and I'd like to limit that list to 5 things (and 4 separating commas).
How can I make a similar function that counts down for the number of commas in the input.
I got this started, but it's not changing the value in the span.
my Javascript
<script language="javascript" type="text/javascript">
var max = 5;
function commaDown(area,ticker){
// our text in the textarea element
var txt = area.val();
// how many commas we have?
var commas = txt.split(",").length;
var span = document.getElementById(ticker);
//var commas ++;
if(commas > max) {
// grab last comma position
var lastComma = txt.lastIndexOf(",");
// delete all after last comma position
area.val(txt.substring(0, lastComma));
//it was count with + 1, so let's take that down
commas--;
}
if (txt == '') {
commas = 0;
}
// show message
span.innerHTML = (max-commas);
}
</script>
and my html (I think the problem lies here)
<input id="choices" type="text" name="choices" class="text medium" onkeyup="commaDown('choices','limit');"/> <span id="limit">5</span><br/>
Any ideas?
Something like this (assuming you have a text field with id csv)
document.getElementById('csv').onkeydown = function(e){
if (!e) var e = window.event;
var list = this.value.split(',');
if (list.length == 5 && e.keyCode == '188' )
{
// what to do if more than 5 commas(,) are entered
// i put a red border and make it go after 1 second
this.style.borderColor ='red';
var _this = this;
setTimeout(function(){
_this.style.borderColor='';
_this.disabled=false;
},1000);
// return false to forbid the surplus comma to be entered in the field
return false;
}
}
example at http://www.jsfiddle.net/gaby/YEHXf/2/
Updated Answer
You seem to have mixed parts of jQuery in your code and that causes the script to fail
var max = 5;
function commaDown(_area, _ticker){
var area = document.getElementById(_area);
// our text in the textarea element
var txt = area.value;
// how many commas we have?
var commas = txt.split(",").length;
var span = document.getElementById(_ticker);
//var commas ++;
if(commas > max) {
// grab last comma position
var lastComma = txt.lastIndexOf(",");
// delete all after last comma position
area.value = txt.substring(0, lastComma);
//it was count with + 1, so let's take that down
commas--;
}
if (txt == '') {
commas = 0;
}
// show message
span.innerHTML = (max-commas);
}
live example at http://jsfiddle.net/z4KRd/
here is a solution:
test: http://jsbin.com/ulobu3
code: http://jsbin.com/ulobu3/edit
if you never used jsBin before, it is very easy, on the left side you have the javascript code (like if it was in your HTML code, and in your right side you have the html code.
and you just need to add /edit to the end of a jsbin url to edit that code, and save any new revisions to that code.
I added jQuery framework to make the example faster to code.