Make lines of text have equal length - javascript

In centered h1 elements, if the text falls on multiple lines, line breaks make the text look like this:
This is a header that takes up two
lines
This is a header that takes up three
lines because it is really, really
long
Is there a way to manipulate these elements so that the length of the lines of text is roughly equal? Like this:
This is a header that
takes up two lines
This is a header that takes
up three lines because it
is really, really long
The jQuery plugin Widow Fix prevents single-word lines, but I'm looking for something that evens out all the lines in a multi-line element. Are there any jQuery plugins for this, or can you recommend a strategy?

I would solve it using only strict JavaScript, going this way:
1. put a class named 'truncate' to h1 tags you want to break
2. configure the javascript code on your needs knowing that
MAXCOUNT: (integer) max chars counted per line
COUNT_SPACES: (boolean) white spaces must be counted?
COUNT_PUNCTUATION: (boolean) punctuation must be counted?
EXACT: (boolean) the last word must be cut?
BLOCKS_CLASS: (string) the className of the h1 to consider
I wrote the code very quickly so it must be better tested for bugs,
but it can be a starting point I think.
I'm not using jQuery in this code to keep the code light and to avoid dependencies.
I think I'm using all cross-browser commands (cannot test it I've got only linux now). However any correction for cross-browser compatibility task (included the use of jQUery if requested) might be easy.
Here is the code:
<html>
<head>
<style>
h1 {background-color: yellow;}
#hiddenDiv {background-color: yellow; display: table-cell; visibility:hidden;}
</style>
<script>
var MAXCOUNT = 20;
var COUNT_SPACES = false;
var EXACT = false;
var COUNT_PUNCTUATION = true;
var BLOCKS_CLASS = 'truncate';
window.onload = function ()
{
var hidden = document.getElementById('hiddenDiv');
if (hidden == null)
{
hidden = document.createElement('div');
hidden.id = 'hiddenDiv';
document.body.appendChild(hidden);
}
var blocks = document.getElementsByClassName(BLOCKS_CLASS);
for (var i=0; i<blocks.length; i++)
{
var block = blocks[i];
var text = block.innerHTML;
var truncate = '';
var html_tag = false;
var special_char = false;
maxcount = MAXCOUNT;
for (var x=0; x<maxcount; x++)
{
var previous_char = (x>0) ? text.charAt(x-1) : '';
var current_char = text.charAt(x);
// Closing HTML tags
if (current_char == '>' && html_tag)
{
html_tag = false;
maxcount++;
continue;
}
// Closing special chars
if (current_char == ';' && special_char)
{
special_char = false;
maxcount++;
continue;
}
// Jumping HTML tags
if (html_tag)
{
maxcount++;
continue;
}
// Jumping special chars
if (special_char)
{
maxcount++;
continue;
}
// Checking for HTML tags
if (current_char == '<')
{
var next = text.substring(x,text.indexOf('>')+1);
var regex = /(^<\w+[^>]*>$)/gi;
var matches = regex.exec(next);
if (matches[0])
{
html_tag = true;
maxcount++;
continue;
}
}
// Checking for special chars
if (current_char == '&')
{
var next = text.substring(x,text.indexOf(';')+1);
var regex = /(^&#{0,1}[0-9a-z]+;$)/gi;
var matches = regex.exec(next);
if (matches[0])
{
special_char = true;
maxcount++;
continue;
}
}
// Shrink multiple white spaces into a single white space
if (current_char == ' ' && previous_char == ' ')
{
maxcount++;
continue;
}
// Jump new lines
if (current_char.match(/\n/))
{
maxcount++;
continue;
}
if (current_char == ' ')
{
// End of the last word
if (x == maxcount-1 && !EXACT) { break; }
// Must I count white spaces?
if ( !COUNT_SPACES ) { maxcount++; }
}
// Must I count punctuation?
if (current_char.match(/\W/) && current_char != ' ' && !COUNT_PUNCTUATION)
{
maxcount++;
}
// Adding this char
truncate += current_char;
// Must I cut exactly?
if (!EXACT && x == maxcount-1) { maxcount++; }
}
hidden.innerHTML = '<h1><nobr>'+truncate+'</nobr></h1>';
block.style.width = hidden.offsetWidth+"px";
}
}
</script>
</head>
<body>
<center>
<h1 class="truncate">
This is a header that
takes up two lines
</h1>
<br>
<h1 class="truncate">
This is a header that takes
up three lines because it
is really, really long
</h1>
<br>
<h1>
This is a header pretty short
or pretty long ... still undecided
which in any case is not truncated!
</h1>
</center>
</body>
</html>
And here is a demo of that: http://jsfiddle.net/6rtdF/

Late to this party, but here's my approach. I get the initial element height (any elements with the class balance_lines, in the code below), then incrementally shrink the width of the element. Once the height of the element changes, I've gone too far. The step before that should have lovely roughly-equal line lengths.
$('.balance_lines').each(function(){
var currentHeight = $(this).height();
var thisHeight = currentHeight;
var currentWidth = $(this).width();
var newWidth = currentWidth;
// Try shrinking width until height changes
while (thisHeight == currentHeight) {
var testWidth = newWidth - 10;
$(this).width(testWidth);
thisHeight = $(this).height();
if (thisHeight == currentHeight) {
newWidth = testWidth;
} else {
break;
}
}
$(this).width(newWidth);
});
You can see this code in action on the homepage at apollopad.com.

The CSS Text 4 draft proposes text-wrap: balance, but I don't think any browser implements it yet.
In the meantime, you can use Adobe's jQuery plugin (demo): https://github.com/adobe-webplatform/balance-text

Related

Angularjs ng-bind-html with custom Filter

I am currently working with ng-bind-html. Basically, what I am trying to do is, when I post a blog, the blog contains links and other styling. So when I am trying to show the list of blogs, I am using ng-bing-html like this:
<p ng-bind-html="blog.blogContent"></p>
which works fine.
But in addition, I try to truncate the blog and show only few paragraphs with view more option by passing a custom filter. But when I pass the filter I get the following:
<p ng-bind-html="blog.blogContent | Truncate"></p>
Error: [$sanitize:badparse] The sanitizer was unable to parse the
following block of html: <a href="https:.......
My Filter looks like this:
return function (text, length, end) {
if (text !== undefined) {
if (isNaN(length)) {
length = 450;
}
if (end === undefined) {
end = ".......";
}
if (text.length <= length || text.length - end.length <= length) {
return text;
} else {
return String(text).substring(0, length - end.length) + end;
}
}
You can solve this using custom directives and filters. try this one: https://stackoverflow.com/a/45076560/6816707
I used the solution posted by Minouris in this post (Javascript truncate HTML text) and adapted it into an AngularJS filter. It seems to work pretty well. The filter is
angular.module('plunker').filter('Truncate', function() {
return function(text, length, end) {
if (text !== undefined) {
if (isNaN(length)) {
length = 20;
}
if (end === undefined) {
end = ".......";
}
if (text.length <= length || text.length - end.length <= length) {
return text;
}
var truncated = text.substring(0, length);
// Remove line breaks and surrounding whitespace
truncated = truncated.replace(/(\r\n|\n|\r)/gm,"").trim();
// If the text ends with an incomplete start tag, trim it off
truncated = truncated.replace(/<(\w*)(?:(?:\s\w+(?:={0,1}(["']{0,1})\w*\2{0,1})))*$/g, '');
// If the text ends with a truncated end tag, fix it.
var truncatedEndTagExpr = /<\/((?:\w*))$/g;
var truncatedEndTagMatch = truncatedEndTagExpr.exec(truncated);
if (truncatedEndTagMatch != null) {
var truncatedEndTag = truncatedEndTagMatch[1];
// Check to see if there's an identifiable tag in the end tag
if (truncatedEndTag.length > 0) {
// If so, find the start tag, and close it
var startTagExpr = new RegExp(
"<(" + truncatedEndTag + "\\w?)(?:(?:\\s\\w+(?:=([\"\'])\\w*\\2)))*>");
var testString = truncated;
var startTagMatch = startTagExpr.exec(testString);
var startTag = null;
while (startTagMatch != null) {
startTag = startTagMatch[1];
testString = testString.replace(startTagExpr, '');
startTagMatch = startTagExpr.exec(testString);
}
if (startTag != null) {
truncated = truncated.replace(truncatedEndTagExpr, '</' + startTag + '>');
}
} else {
// Otherwise, cull off the broken end tag
truncated = truncated.replace(truncatedEndTagExpr, '');
}
}
// Now the tricky part. Reverse the text, and look for opening tags. For each opening tag,
// check to see that he closing tag before it is for that tag. If not, append a closing tag.
var testString = reverseHtml(truncated);
var reverseTagOpenExpr = /<(?:(["'])\w*\1=\w+ )*(\w*)>/;
var tagMatch = reverseTagOpenExpr.exec(testString);
while (tagMatch != null) {
var tag = tagMatch[0];
var tagName = tagMatch[2];
var startPos = tagMatch.index;
var endPos = startPos + tag.length;
var fragment = testString.substring(0, endPos);
// Test to see if an end tag is found in the fragment. If not, append one to the end
// of the truncated HTML, thus closing the last unclosed tag
if (!new RegExp("<" + tagName + "\/>").test(fragment)) {
truncated += '</' + reverseHtml(tagName) + '>';
}
// Get rid of the already tested fragment
testString = testString.replace(fragment, '');
// Get another tag to test
tagMatch = reverseTagOpenExpr.exec(testString);
}
return truncated;
}
}
function reverseHtml(str) {
var ph = String.fromCharCode(206);
var result = str.split('').reverse().join('');
while (result.indexOf('<') > -1) {
result = result.replace('<',ph);
}
while (result.indexOf('>') > -1) {
result = result.replace('>', '<');
}
while (result.indexOf(ph) > -1) {
result = result.replace(ph, '>');
}
return result;
}
});
Working plunkr:
http://plnkr.co/edit/oCwmGyBXB26omocT2q9m?p=preview
I havent tested the above solution and you may run into issues with more complicated HTML strings. May I suggest using a Jquery library like https://github.com/pathable/truncate to be safe?

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();
});

Word wrap: ellipsis without css

I'm developing an app for a TV with an old Gecko engine (1.9 I think). I have a container 250x40 and I'd like to fit 2 lines of text in it, and if it's too long then an ellipsis should be shown (just like in the CSS3 property text-overflow: ellipsis).
However:
- I cannot use CSS3,
- I tried using some jQuery plugins, but they just work too slow - you can accually see the text being shrunk down until it fits in the container.
I tried counting letters, but it doesn't work, because they are all different widths.
I tried mapping each letter to its width, and counting the widht of the whole text, but the fact that it's 2 lines screws it all up - I don't know at which point the text will go to the next line.
Any help appreciated.
Slightly based on #Emissary's answer, here's a reasonably performing pair of jQuery plugins that'll handle adding ellipsis on elements that might hold more than one row of text:
(function($) {
$.fn.reflow = function() {
return this.each(function() {
var $this = $(this);
var $parent = $this.parent();
var text = $this.data('reflow');
$this.text(text); // try full text again
var words = text.split(' ');
while ($this.height() > $parent.height() ||
$this.width() > $parent.width()) {
words.pop();
$this.html(words.join(' ') + '…');
}
});
}
$.fn.overflow = function() {
return this.each(function() {
var $this = $(this);
var text = $this.text();
$this.data('reflow', text);
}).reflow();
}
})(jQuery);
The latter one registers elements for reflowing, and the first one actually does the work. The split is there to allow window resizing to automatically reformat the (original) contents of the element, e.g.:
$('.overflow').overflow();
$(window).on('resize', function() {
$('.overflow').reflow();
});
For higher performance, if it matters, you might consider replacing the .pop sequence with something that uses a O(log n) binary partitioning algorithm to find the optimal cut point instead of just removing one word at a time.
See http://jsfiddle.net/alnitak/vyth5/
It's been a while since I bothered with supporting older browsers but this is how I always did it. Should point out that it trims words rather than characters, I always thought half a word looked daft - if you care about typography...
html:
<div id="el">
<span class="overflow">Lorem ipsum dolor sit amet</span>
</div>
css:
#el {
width: 250px;
height: 40px;
overflow: hidden;
}
.overflow {
white-space: nowrap;
}
js / jQuery:
var el = $('#el'),
ov = $('#el .overflow'),
w = el.text().split(' ');
while(ov.width() > el.width()){
w.pop();
ov.html( w.join(' ') + '…' );
}
jsFiddle
This chap here has a solution that uses javascript and no JQuery: http://blog.mastykarz.nl/measuring-the-length-of-a-string-in-pixels-using-javascript/
Done in jsfiddle: http://jsfiddle.net/ZfDYG/
Edit - just read the bit about 2 lines of text: http://jsfiddle.net/ZfDYG/8/
code (for completeness):
String.prototype.visualLength = function() {
var ruler = $("ruler");
ruler.innerHTML = this;
return ruler.offsetWidth;
}
function $(id) {
return document.getElementById(id);
}
var s = "Some text that is quite long and probably too long to fit in the box";
var len = s.visualLength();
String.prototype.trimToPx = function(length,postfix) {
postfix = postfix || '';
var tmp = this;
var trimmed = this;
if (tmp.visualLength() > length) {
trimmed += postfix;
while (trimmed.visualLength() > length) {
tmp = tmp.substring(0, tmp.length-1);
trimmed = tmp + postfix;
}
}
return trimmed;
}
String.prototype.wrapToLength = function(complete) {
if(complete[this.length] == ' ' || complete[this.length - 1] == ' ') return;
var wrapped = this;
var found = false;
for(var i = this.length-1; i > 0 && !found; i--) {
if(this[i] == ' ') {
wrapped = this.substring(0, i);
found = true;
}
}
return wrapped;
}
var firstLine = s.trimToPx(240).wrapToLength(s);
var secondLine = s.substring(firstLine.length, s.length);
$('output').innerHTML= firstLine+' '+secondLine.trimToPx(240,'...');
Html:
<span id="ruler"></span>
<div id="output"></div>
css:
#ruler { visibility: hidden; white-space: nowrap; }
#output {
width: 240px;
height: 50px;
border: 1px solid red;
}
If that is still too slow on your box, I guess it should be possible to speed up the while loop by starting with bigger additions to oscillate towards the final length (kinda like a spring) rather than increment slowly up to it.

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