How to restrict number of characters per line in textarea? - javascript

I have one Ext JS TextArea. There I want to restrict my characters to 15 characters in each line and total number of lines should be not more than 10.
What I am tring to do here is
function(){
var myValue = this.getValue();
var myValueData = myValue.split(/\r*\n/);
myValueData.length = 10;
}
Ideally it should ommit all the lines after line number 10, but is not happening. Also how to restrict only upto 15 characters per line?

You can give that one a try, not perfect, but should work.
Maybe its be better instead using the change listeners, overwriting setValue or setRawValue functions on the component.
https://fiddle.sencha.com/#view/editor&fiddle/2js1
{
xtype: 'textareafield',
grow: true,
name: 'message',
fieldLabel: 'Message',
anchor: '100%',
listeners: {
change: function (cmp, newVal, oldVal) {
var arrayForEachLine = newVal.split(/\r\n|\r|\n/g);
var newVal = [];
Ext.Array.each(arrayForEachLine, function (line, index) {
if (line.length >= 10) {
offset = line;
while (offset.length > 0) {
newVal.push(offset.substring(0, 10));
offset = offset.substring(10);
}
} else {
newVal.push(line);
}
if (index === 10) return false;
if (newVal.length >= 10) {
newVal = newVal.slice(0, 10);
return false;
}
});
cmp.suspendEvent('change');
cmp.setValue(newVal.join('\n'));
cmp.resumeEvent('change');
}
}
}

This can be achieved by using the change listener. I have created a demo (fiddle). Please look at the fiddle as the code there is neat and has comments too. Please look at my change listener code below:
listeners: {
change: function () {
var myValue = Ext.getCmp('myField').getValue();
var lines = myValue.split("\n");
if (lines.length > 10) {
alert("You've exceeded the 10 line limit!");
Ext.getCmp('myField').setValue(textAreaValue);
} else { //if num of lines <= 10
for (var i = 0; i < lines.length; i++) { /
if (lines[i].length > 15) { /
alert("Length exceeded in line " + (i+1));
Ext.getCmp('myField').setValue(textAreaValue);
} else if (lines[i].length <= 15 && i == lines.length - 1) {
textAreaValue = myValue;
}
}
}
}
}
Please mark this as an answer if this answer solved your problem as this will also help other people in future. If there is anything wrong with this answer please let me know in the comments. I am sorry if something's wrong as I am not an extjs expert. But I still tried.

This problem is prefect for regex...
My solution focuses on the "max char width" and "max line number" requirements which I don't consider to be the same as "max char count" due to wrapping on words. Because this solution uses plain regex it works best with plain-text with newline chars \n for line breaks rather than html.
This function is idempotent which means it's output can be piped into it's input without issue. It's also very efficient because it's pure regex. Both of these attributes make it perfect for dropping into an onchange handler to update text on every key-press without incurring large performance penalties.
const fit = (str, w = 80, h = 24) => {
w = new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, 'g');
str = str.replace(w, '$1\n');
h = new RegExp(`(((^|\\n)[^\\n]*){${h}})((($|\\n)[^\\n]*)+)`);
str = str.replace(h, '$1');
return str;
};
This function will format the string into the given width / height, but it does so in a very agreeable way: The wrapping regex is considerate of existing new lines which is why it's idempotent, and breaks lines between words not through them. For an in depth explanation of how the word wrap regex works and why it's robust, see my answer for word wrapping strings in JavaScript with regex:
Wrap Text In JavaScript

To restrict textarea character length you can use the following attribute maxlength .
<textarea maxlength="50">
To truncate a string or number.
text_truncate = function(str, length, ending) {
if (length == null) {
length = 100;
}
if (ending == null) {
ending = '...';
}
if (str.length > length) {
return str.substring(0, length - ending.length) + ending;
} else {
return str;
}
};
you can call it by
text_truncate(yourContentToTruncate,15);

This solution is using pure JavaScript.
The basic idea is creating an array of chunks made of 15 characters with .match(/.{1,10}/g) and then, joining the array with a line break join("\n") to create an string again. The number of lines are restricted just removing the remaining chunks using splice(10, chunks.length - 1).
We are using .onkeyup event, but could be another event, that's because the code is not fired until we release a key. But this code also works if we paste text inside the textarea because the event is fired as well.
document.getElementById("textArea").onkeyup = function() {
var text = this.value.replace("\n", "");
if (text.length > 15) {
var chunks = text.match(/.{1,15}/g);
if (chunks.length > 10) {
chunks.splice(10, chunks.length - 1);
alert("if text have more than 10 lines is removed");
}
this.value = chunks.join("\n");
}
};
<textarea rows="18" cols="50" id="textArea">
</textarea>

In the place where you are specifying the textarea field, you need to set 'enforceMaxLength' attribute to true and then set 'maxLength' attribute to number of characters as your limit. Please find the following sample code for extjs. In the example i have set the restriction of no more than 10 characters. Hope this helps.
Ext.create('Ext.form.Panel', {
title: 'Contact Info',
width: 300,
bodyPadding: 10,
renderTo: Ext.getBody(),
items: [{
xtype: 'textarea',
name: 'name',
fieldLabel: 'Name',
enforceMaxLength:true,
maxLength:10,
allowBlank: false // requires a non-empty value
}]
});

for 10 line accept condition
$(document).ready(function(){
var lines = 10;
var linesUsed = $('#linesUsed');
$('#countMe').keydown(function(e) {
newLines = $(this).val().split("\n").length;
linesUsed.text(newLines);
if(e.keyCode == 13 && newLines >= lines) {
linesUsed.css('color', 'red');
return false;
}
else {
linesUsed.css('color', '');
}
});
});

I have written the solution which puts cuted characters to the start of the next line. Only 10 lines are allowed, in each line 15 characters.
Solution with Ext JS
Unfortunately, snippets on SO do not work with Ext JS.
And because of this you can see it on this codepen.io link.
Ext.application(
{
name: 'ExtApp',
launch: function ()
{
Ext.create('Ext.form.Panel',
{
title: 'Sample TextArea',
bodyPadding: 10,
renderTo: Ext.getBody(),
title: 'Restricted number of characters per line in textarea',
height: 500,
width: 700,
items:
[{
xtype: 'textareafield',
grow: true,
fieldLabel: 'Please write your text:',
height: 320,
width: 480,
listeners:
{
change: function(self, newVal)
{
var chunks = newVal.split(/\r*\n/g),
lastLineCutedTxt = '';
for(var i = 0; i < chunks.length; i++)
{
chunks[i] = lastLineCutedTxt + chunks[i];
if(chunks[i].length > 15)
{
lastLineCutedTxt = chunks[i].slice(15);
chunks[i] = chunks[i].slice(0, 15);
}
else lastLineCutedTxt = '';
}
if(lastLineCutedTxt != '')
chunks.push(lastLineCutedTxt);
self.suspendEvent('change');
self.setValue(chunks.slice(0, 10).join('\n'));
self.resumeEvent('change');
}
}
}]
});
}
});
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script>
<link rel="stylesheet" type="text/css" href="http://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css">
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/theme-triton.js"></script>
Solution with pure JavaScript
var txtArea = document.getElementById('txt-area')
txtArea.onkeyup = function(e)
{
var chunks = this.value.split(/\r*\n/g),
lastLineCutedTxt = '';
for(var i = 0; i < chunks.length; i++)
{
chunks[i] = lastLineCutedTxt + chunks[i];
if(chunks[i].length > 15)
{
lastLineCutedTxt = chunks[i].slice(15);
chunks[i] = chunks[i].slice(0, 15);
}
else lastLineCutedTxt = '';
}
if(lastLineCutedTxt != '')
chunks.push(lastLineCutedTxt);
this.value = chunks.slice(0, 10).join('\n');
};
<textarea id="txt-area" rows="16" cols="35"></textarea>

Related

Max lines textarea

I have found some scripts that limit the lines used in a textarea like this:
$(document).ready(function(){
var lines = 10;
var linesUsed = $('#linesUsed');
var newLines=0;
$('#rev').keydown(function(e) {
newLines = $(this).val().split("\n").length;
linesUsed.text(newLines);
if(e.keyCode == 13 && newLines >= lines) {
linesUsed.css('color', 'red');
return false;
}
else {
linesUsed.css('color', '');
}
});
It works fine when you hit enter and limits it to 10 .But the problem occurs when you type sentences that are so long they automatically go to a new line without the \n and when you copy paste a text, then it fails to limit the lines used.
does anyone know how to fix this.
Important: solution needs to work for a textarea
You could try doing it using this logic:
JS :
var limit = 3; // <---max no of lines you want in textarea
var textarea = document.getElementById("splitLines");
var spaces = textarea.getAttribute("cols");
textarea.onkeyup = function() {
var lines = textarea.value.split("\n");
for (var i = 0; i < lines.length; i++)
{
if (lines[i].length <= spaces) continue;
var j = 0;
var space = spaces;
while (j++ <= spaces)
{
if (lines[i].charAt(j) === " ") space = j;
}
lines[i + 1] = lines[i].substring(space + 1) + (lines[i + 1] || "");
lines[i] = lines[i].substring(0, space);
}
if(lines.length>limit)
{
textarea.style.color = 'red';
setTimeout(function(){
textarea.style.color = '';
},500);
}
textarea.value = lines.slice(0, limit).join("\n");
};
Here is the UPDATED DEMO
Well, I couldn't figure out how to calculate the height of only the text inside a textarea, so I used a contenteditable div instead. Hope you like this solution.
HTML
<div id="container">
<div id="rev" contenteditable="true"></div>
</div>
CSS
#container {
height:100px;
width:300px;
cursor:text;
border:1px solid #000
}
#rev {
line-height:20px;
outline:none
}
JS
$(document).ready(function () {
$('#container').click(function() {
$('#rev').focus();
});
var limit = 3;
var lineHeight = parseInt($('#rev').css('line-height'));
$('#rev').keydown(function (e) {
var totalHeight = parseInt($('#rev').height());
var linesUsed = totalHeight / lineHeight;
if (e.keyCode == 13 && linesUsed >= limit) {
$('#rev').css('color', 'red');
return false;
} else {
$('#rev').css('color', '');
}
});
});
HERE IS A DEMO YOU CAN FIDDLE WITH
MAJOR EDIT
Following the OP pointing out I actually forgot to address the most important, I updated my code. I basically removed the check for the enter key and allowed the delete and backspace keys in case the text goes over the limit as follows. You may have to fiddle around with it a little to make it fit to your exact needs.
$(document).ready(function () {
$('#container').click(function() {
$('#rev').focus();
});
var limit = 3;
var lineHeight = parseInt($('#rev').css('line-height'));
$('#rev').keydown(function (e) {
var totalHeight = parseInt($('#rev').height());
var linesUsed = totalHeight / lineHeight;
if (linesUsed > limit) { // I removed 'e.keyCode == 13 &&' from here
$('#rev').css('color', 'red');
if (e.keyCode != 8 && e.keyCode != 46) return false; // I added this check
} else {
$('#rev').css('color', '');
}
});
// I added the following lines
$('#rev').on('paste', function () {
if (linesUsed > limit) {
$('#rev').css('color', 'red');
if (e.keyCode != 8 && e.keyCode != 46) return false;
} else {
$('#rev').css('color', '');
}
});
});
UPDATED DEMO HERE
It's too much work to try to figure how many lines based on the number characters in each line and the textarea width (does the textarea have wrapping off or not? font size, different letter widths, spaces, etc...). The easiest way is to have two textareas (one visible and one not - height set to 0) with the same width and font styles and check the scroll height of the invisible textarea.
Here is an example http://jsfiddle.net/SKYt4/1/
HTML
<textarea id="visible_textarea"></textarea>
<textarea id="hidden_textarea"></textarea> <!-- hidden by setting the height to 0 -->
<div id="used_lines"></div>
CSS
textarea {line-height:16px; /* line height to calculate number of lines */
width:150px; /* need to match width */}
#hidden_textarea {height:0px;
padding:0px;
border:none;
margin:0px;
opacity:0;}
JavaScript
$('#visible_textarea').keyup(function(){
$('#hidden_textarea').val(this.value);
// checking how many lines
var lns = Math.ceil(document.getElementById('hidden_textarea').scrollHeight / parseInt($('#hidden_textarea').css('line-height')));
if (lns > 10) {
$('#used_lines').css('color', '#ff0000');
}
else {
$('#used_lines').css('color', '');
}
$('#used_lines').html(lns+' lines');
});
$('#visible_textarea').change(function(){
$(this).keyup();
});

jQuery "keyup" crashing page when checking 'Word Count'

I have a word counter running on a DIV and after typing in a few words, the page crashes. The browser continues to work (par scrolling) and no errors are showing in Chrome's console. Not sure where I'm going wrong...
It all started when I passed "wordCount(q);" in "keyup". I only passed it there as it would split-out "NaN" instead of a number to countdown from.
JS:
wordCount();
$('#group_3_1').click(function(){
var spliced = 200;
wordCount(spliced);
}) ;
$('#group_3_2').click(function(){
var spliced = 600;
wordCount(spliced);
}) ;
function wordCount(q) {
var content_text = $('.message1').text(),
char_count = content_text.length;
if (char_count != 0)
var word_count = q - content_text.replace(/[^\w ]/g, "").split(/\s+/).length;
$('.word_count').html(word_count + " words remaining...");
$('.message1').keyup(function() {
wordCount(q);
});
try
{
if (new Number( word_count ) < 0) {
$(".word_count").attr("id","bad");
}
else {
$(".word_count").attr("id","good");
}
} catch (error)
{
//
}
};
HTML:
<input type="checkbox" name="entry.3.group" value="1/6" class="size1" id="group_3_1">
<input type="checkbox" name="entry.3.group" value="1/4" class="size1" id="group_3_2">
<div id="entry.8.single" class="message1" style="height: 400px; overflow-y:scroll; overflow-x:hidden;" contenteditable="true"> </div>
<span class="word_count" id="good"></span>
Thanks in advanced!
This is causing an infinite loop if (new Number(word_count) < 0) {.
Your code is a mess altogether. Just study and start with more basic concepts and start over. If you want to describe your project to me in a comment, I would be glad to show you a good, clean, readable approach.
Update:
Part of having a good architecture in your code is to keep different parts of your logic separate. No part of your code should know about or use anything that isn't directly relevant to it. Notice in my word counter that anything it does it immediately relevant to its word-counter-ness. Does a word counter care about what happens with the count? Nope. It just counts and sends the result away (wherever you tell it to, via the callback function). This isn't the only approach, but I just wanted to give you an idea of how to approach things more sensefully.
Live demo here (click).
/* what am I creating? A word counter.
* How do I want to use it?
* -Call a function, passing in an element and a callback function
* -Bind the word counter to that element
* -When the word count changes, pass the new count to the callback function
*/
window.onload = function() {
var countDiv = document.getElementById('count');
wordCounter.bind(countDiv, displayCount);
//you can pass in whatever function you want. I made one called displayCount, for example
};
var wordCounter = {
current : 0,
bind : function(elem, callback) {
this.ensureEditable(elem);
this.handleIfChanged(elem, callback);
var that = this;
elem.addEventListener('keyup', function(e) {
that.handleIfChanged(elem, callback);
});
},
handleIfChanged : function(elem, callback) {
var count = this.countWords(elem);
if (count !== this.current) {
this.current = count;
callback(count);
}
},
countWords : function(elem) {
var text = elem.textContent;
var words = text.match(/(\w+\b)/g);
return (words) ? words.length : 0;
},
ensureEditable : function(elem) {
if (
elem.getAttribute('contenteditable') !== 'true' &&
elem.nodeName !== 'TEXTAREA' &&
elem.nodeName !== 'INPUT'
) {
elem.setAttribute('contenteditable', true);
}
}
};
var display = document.getElementById('display');
function displayCount(count) {
//this function is called every time the word count changes
//do whatever you want...the word counter doesn't care.
display.textContent = 'Word count is: '+count;
}
I would do probably something like this
http://jsfiddle.net/6WW7Z/2/
var wordsLimit = 50;
$('#group_3_1').click(function () {
wordsLimit = 200;
wordCount();
});
$('#group_3_2').click(function () {
wordsLimit = 600;
wordCount();
});
$('.message1').keydown(function () {
wordCount();
});
function wordCount() {
var text = $('.message1').text(),
textLength = text.length,
wordsCount = 0,
wordsRemaining = wordsLimit;
if(textLength > 0) {
wordsCount = text.replace(/[^\w ]/g, '').split(/\s+/).length;
wordsRemaining = wordsRemaining - wordsCount;
}
$('.word_count')
.html(wordsRemaining + " words remaining...")
.attr('id', (parseInt(wordsRemaining) < 0 ? 'bad' : 'good'));
};
wordCount();
It's not perfect and complete but it may show you direction how to do this. You should use change event on checkboxes to change wordsLimit if checked/unchecked. For styling valid/invalid words remaining message use classes rather than ids.
I think you should use radio in place of checkboxes because you can limit 200 or 600 only at a time.
Try this like,
wordCount();
$('input[name="entry.3.group"]').click(function () {
wordCount();
$('.word_count').html($(this).data('val') + " words remaining...");
});
$('.message1').keyup(function () {
wordCount();
});
function wordCount() {
var q = $('input[name="entry.3.group"]:checked').data('val');
var content_text = $('.message1').text(),
char_count = content_text.length;
if (char_count != 0) var word_count = q - content_text.replace(/[^\w ]/g, "").split(/\s+/).length;
$('.word_count').html(word_count + " words remaining...");
try {
if (Number(word_count) < 0) {
$(".word_count").attr("id", "bad");
} else {
$(".word_count").attr("id", "good");
}
} catch (error) {
//
}
};
Also you can add if your span has bad id then key up should return false;
See Demo

Can I create an HTML input field that stops accepting characters when it is "full"?

I would like to create HTML input fields (either single-line or textarea) that stop accepting new text when they are "full" - i.e. when there is no more room in the widget for new characters to be displayed.
This is different from a putting a maxlength limit on the number of characters, as (for example) 65 lowercase letter "i"s take up much less space than 65 uppercase "W"s in proportional fonts.
Is there a way to do this? (If there is, the solution would ideally also cover when text is pasted in, and truncate all the text over the limit).
Edit: The solution has to work with proportional fonts - maxlength does not help here, we are not interested in stopping at a character length.
The following uses the answer from https://stackoverflow.com/a/5047712/212869 to calculate the width of text, and I linked it to a textbox. Its really rough code :D but it does limit the textbox fairly well. The same principle could be used on a text area, but you would also have do it for both height and width.
http://jsfiddle.net/LMKtd/1/ - it outputs stuff to console so best to have it open if you have a look.
String.prototype.width = function(font) {
var f = font || '12px arial',
o = $('<div>' + this + '</div>')
.css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font': f})
.appendTo($('body')),
w = o.width();
o.remove();
return w;
}
$('#textinput').on('keyup', validatetext);
function validatetext(e) {
var w = parseInt(e.target.value.width());
if (w > 100) {
console.log("Width Gt 100px ["+w+"px] Char Count ["+e.target.value.length+"]");
do {
e.target.value = e.target.value.slice(0,-1);
} while (parseInt(e.target.value.width()) > 100)
} else {
console.log("Keep going! ["+w+"px] Char Count ["+e.target.value.length+"]");
}
}
Update
http://jsfiddle.net/LMKtd/8/
I've bodged together one for the text area too. It doesnt stop you going over the limits but it tells you when its too wide or tall. It's not very pretty :D
String.prototype.width = function(font) {
var f = font || '12px arial',
o = $('<div>' + this + '</div>')
.css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'visible', 'font': f})
.appendTo($('body')),
w = o.width();
o.remove();
return w;
}
String.prototype.height = function(font) {
var f = font || '12px arial',
o = $('<div>' + this.replace(/[\r\n]/g,'<br />') + '</div>')
.css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font': f})
.appendTo($('body')),
w = o.height();
o.remove();
return w;
}
$('#textinput').on('keyup', validatetext);
$('#areainput').keyup(validatearea);
function validatearea(e) {
var w = parseInt($(this).val().width());
var h = parseInt($(this).val().height());
var errw = false;
var errh = false;
if (h>100) {
errh = true
}
var lines = $(this).val().split(/[\r\n]/);
var errw = false;
for (var i = 0; i<lines.length; i++) {
if (parseInt(lines[i].width()) > 100) {
errw = true;
}
}
if ((errh == true) || (errw == true)) {
if ((errh == true) && (errw == false)) {
$('#areaerror').html("Too Tall, Width OK");
}
if ((errh == false) && (errw == true)) {
$('#areaerror').html("Height OK, Too Wide");
}
if ((errh == true) && (errw == true)) {
$('#areaerror').html("Too Tall, Too Wide");
}
} else {
$('#areaerror').html("Were Good");
}
}
function validatetext(e) {
var w = parseInt(e.target.value.width());
if (w > 100) {
console.log("Width Gt 100px ["+w+"px] Char Count ["+e.target.value.length+"]");
do {
e.target.value = e.target.value.slice(0,-1);
} while (parseInt(e.target.value.width()) > 100)
} else {
console.log("Keep going! ["+w+"px] Char Count ["+e.target.value.length+"]");
}
}
You can use the maxlength attribute
<input type="text" id="Textbox" name="Textbox" maxlength="10" />
How about jquery inputfit plugin ? check the demo and source
You might change the source to accommodate your requirements. Obviously you have to use jquery library an may not be suitable if you are searching for a solution in plain-JavaScript.
check the options (min size, max size) they seems fit for your requirement

Find out the 'line' (row) number of the cursor in a textarea

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.

Javascript Limit Commas

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.

Categories