24

I'm writing a mobile web application where scrollbars are not displayed on the device's browser. Due to this, I'm trying to dynamically modify the height of the textarea to make it bigger, however I don't know of any way to actually get the line count on an html textarea. Any help would be greatly appreciated!

EDIT

So I realize now that it's not newlines per se, but actual line wrapping. So when one line finishes it wraps the text to the next line. It appears as if it is a new line. Any way to count the number of these? Thanks!

Daniel O'Hara
  • 13,307
  • 3
  • 46
  • 68
Kyle
  • 607
  • 2
  • 6
  • 14

9 Answers9

15

The number of lines in the textarea would be

textarea.value.match(/\n/g).length + 1
Delan Azabani
  • 79,602
  • 28
  • 170
  • 210
  • 1
    I'm doing $(nameofthetextareaid).val().match(/\n/g) and am receiving null from the debugger. I tried it with /\n/g in quotes as well. – Kyle Sep 12 '10 at 23:35
  • Try the plain JavaScript solution I gave you, instead of using jQuery? – Delan Azabani Sep 12 '10 at 23:35
  • 5
    I realize now that it's line wrapping not new lines. The solution you gave did work though for newlines. – Kyle Sep 12 '10 at 23:42
11

I have created a plugin to handle line counting and wrap detection in a <textarea>.
I hope someone can use it.

Code on BitBucket

Sample Usage

var result = $.countLines("#textarea");

result.actual   // The number of lines in the textarea.
result.wraps    // The number of lines in the textarea that wrap at least once.
result.wrapped  // The total number of times all lines wrap.
result.blank    // The number of blank lines.
result.visual   // The approximate number of lines that the user actually sees in the textarea  

Working Demonstration

/*! Textarea Line Count - v1.4.1 - 2012-12-06
 * https://bitbucket.org/MostThingsWeb/textarea-line-count
 * Copyright (c) 2012 MostThingsWeb (Chris Laplante); Licensed MIT */

(function($) {
  $.countLines = function(ta, options) {
    var defaults = {
      recalculateCharWidth: true,
      charsMode: "random",
      fontAttrs: ["font-family", "font-size", "text-decoration", "font-style", "font-weight"]
    };

    options = $.extend({}, defaults, options);

    var masterCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var counter;

    if (!ta.jquery) {
      ta = $(ta);
    }

    var value = ta.val();
    switch (options.charsMode) {
      case "random":
        // Build a random collection of characters
        options.chars = "";
        masterCharacters += ".,?!-+;:'\"";
        for (counter = 1; counter <= 12; counter++) {
          options.chars += masterCharacters[(Math.floor(Math.random() * masterCharacters.length))];
        }
        break;
      case "alpha":
        options.chars = masterCharacters;
        break;
      case "alpha_extended":
        options.chars = masterCharacters + ".,?!-+;:'\"";
        break;
      case "from_ta":
        // Build a random collection of characters from the textarea
        if (value.length < 15) {
          options.chars = masterCharacters;
        } else {
          for (counter = 1; counter <= 15; counter++) {
            options.chars += value[(Math.floor(Math.random() * value.length))];
          }
        }
        break;
      case "custom":
        // Already defined in options.chars
        break;
    }

    // Decode chars
    if (!$.isArray(options.chars)) {
      options.chars = options.chars.split("");
    }

    // Generate a span after the textarea with a random ID
    var id = "";
    for (counter = 1; counter <= 10; counter++) {
      id += (Math.floor(Math.random() * 10) + 1);
    }

    ta.after("<span id='s" + id + "'></span>");
    var span = $("#s" + id);

    // Hide the span
    span.hide();

    // Apply the font properties of the textarea to the span class
    $.each(options.fontAttrs, function(i, v) {
      span.css(v, ta.css(v));
    });

    // Get the number of lines
    var lines = value.split("\n");
    var linesLen = lines.length;

    var averageWidth;

    // Check if the textarea has a cached version of the average character width
    if (options.recalculateCharWidth || ta.data("average_char") == null) {
      // Get a pretty good estimation of the width of a character in the textarea. To get a better average, add more characters and symbols to this list
      var chars = options.chars;

      var charLen = chars.length;
      var totalWidth = 0;

      $.each(chars, function(i, v) {
        span.text(v);
        totalWidth += span.width();
      });

      // Store average width on textarea
      ta.data("average_char", Math.ceil(totalWidth / charLen));
    }

    averageWidth = ta.data("average_char");

    // We are done with the span, so kill it
    span.remove();

    // Determine missing width (from padding, margins, borders, etc); this is what we will add to each line width
    var missingWidth = (ta.outerWidth() - ta.width()) * 2;

    // Calculate the number of lines that occupy more than one line
    var lineWidth;

    var wrappingLines = 0;
    var wrappingCount = 0;
    var blankLines = 0;

    $.each(lines, function(i, v) {
      // Calculate width of line
      lineWidth = ((v.length + 1) * averageWidth) + missingWidth;
      // Check if the line is wrapped
      if (lineWidth >= ta.outerWidth()) {
        // Calculate number of times the line wraps
        var wrapCount = Math.floor(lineWidth / ta.outerWidth());
        wrappingCount += wrapCount;
        wrappingLines++;
      }

      if ($.trim(v) === "") {
        blankLines++;
      }
    });

    var ret = {};
    ret["actual"] = linesLen;
    ret["wrapped"] = wrappingLines;
    ret["wraps"] = wrappingCount;
    ret["visual"] = linesLen + wrappingCount;
    ret["blank"] = blankLines;

    return ret;
  };
}(jQuery));



result = jQuery.countLines("#textarea");

jQuery('#display').html(
  '<span>Actual: ' + result.actual + '</span>' +
  '<span>Blank: ' + result.blank + '</span>' +
  '<span>Visual: ' + result.visual + '</span>' +
  '<span>Wrapped: ' + result.wrapped + '</span>' +
  '<span>Wraps: ' + result.wraps + '</span>'
);
#textarea {
  width: 150px;
  height: 80px;
}
#display span {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="textarea">text
here
  
this is a longer line so that it will wrap in the box longer longer longer</textarea>

<div id="display"></div>
showdev
  • 28,454
  • 37
  • 55
  • 73
Chris Laplante
  • 29,338
  • 17
  • 103
  • 134
  • It would be great if you add your code to your answer so that your solution is not reliant on unreliable external links. – showdev Sep 15 '15 at 17:10
  • @showdev: Sorry for the bad link. Use this: https://bitbucket.org/MostThingsWeb/textarea-line-count – Chris Laplante Sep 15 '15 at 22:44
  • 1
    Thanks @ChrisLaplante. Very cool plugin. I took the liberty of adding a working demonstration to your post. Feel free to roll back the edit if you don't like it. – showdev Sep 16 '15 at 00:15
9

This is an efficient and accurate method to count the number of lines in a text area, including wrapped lines.

/** @type {HTMLTextAreaElement} */
var _buffer;

/**
* Returns the number of lines in a textarea, including wrapped lines.
*
* __NOTE__:
* [textarea] should have an integer line height to avoid rounding errors.
*/
function countLines(textarea) {
    if (_buffer == null) {
        _buffer = document.createElement('textarea');
        _buffer.style.border = 'none';
        _buffer.style.height = '0';
        _buffer.style.overflow = 'hidden';
        _buffer.style.padding = '0';
        _buffer.style.position = 'absolute';
        _buffer.style.left = '0';
        _buffer.style.top = '0';
        _buffer.style.zIndex = '-1';
        document.body.appendChild(_buffer);
    }

    var cs = window.getComputedStyle(textarea);
    var pl = parseInt(cs.paddingLeft);
    var pr = parseInt(cs.paddingRight);
    var lh = parseInt(cs.lineHeight);

    // [cs.lineHeight] may return 'normal', which means line height = font size.
    if (isNaN(lh)) lh = parseInt(cs.fontSize);

    // Copy content width.
    _buffer.style.width = (textarea.clientWidth - pl - pr) + 'px';

    // Copy text properties.
    _buffer.style.font = cs.font;
    _buffer.style.letterSpacing = cs.letterSpacing;
    _buffer.style.whiteSpace = cs.whiteSpace;
    _buffer.style.wordBreak = cs.wordBreak;
    _buffer.style.wordSpacing = cs.wordSpacing;
    _buffer.style.wordWrap = cs.wordWrap;

    // Copy value.
    _buffer.value = textarea.value;

    var result = Math.floor(_buffer.scrollHeight / lh);
    if (result == 0) result = 1;
    return result;
}

Demo here

Kin
  • 1,522
  • 1
  • 13
  • 11
  • 1
    This is excellent! Besides standard ASCII, I tested this with UTF-8 emoji and foreign language graphemes, and it works flawlessly. – Mac Apr 29 '18 at 04:02
  • You are missing set fontSize and fontFamily to _buffer. At least i had problem whit it but excellent :) – Yakub Nov 01 '20 at 20:09
7

I haven't tried using the function discussed in this blog, but you may find it useful.

http://kirblog.idetalk.com/2010/03/calculating-cursor-position-in-textarea.html

Basically, if you create a div and then copy the text into that div, with the same width and font characteristics, you can then get the information you need, such as the number of lines. The number of lines in this example would be easy, in that if you know how many pixels high a single line would be, then just find the width of the test div and you can get a pretty accurate idea as to how many lines are in your textarea.

James Black
  • 41,583
  • 10
  • 86
  • 166
  • 3
    This will be close but not exact. There will still be minor differences in some browsers for a textarea vs a div, such as implicit padding inside the textarea that cannot be removed unless the border is removed (Firefox), or the presence of a disabled vertical scrollbar unless overflow:hidden is set (IE). Temporarily changing these textarea styles for consistency with a div would change the effective width of the textarea and skew the results as much as not changing them would. I suppose it might be more accurate if browser-sniffing is used to customize the div, if it's worth the trouble. – Matt Coughlin Apr 05 '13 at 14:54
  • @MattCoughlin - You are correct, it won't be exact, which is why I went with "pretty accurate idea". :) – James Black Apr 06 '13 at 17:51
3

Get scrollHeight, subtract top+bottom padding, divide by lineHeight.

Tgr
  • 27,442
  • 12
  • 81
  • 118
1

I'm pretty sure there is no reasonable way to count the number of lines as displayed in the browser especially considering some browsers (Safari) allow the user to resize textareas.

It'd be hacky, but your best bet might be to just estimate based on the total characters divided by average number of characters per line. :-/

tfe
  • 32,666
  • 2
  • 27
  • 24
  • That's what I tried before, but it always messed up in special cases – Kyle Sep 12 '10 at 23:44
  • Yeah, that's why I said "hacky" and "estimate." :-/ I don't think there's any other way to do it given the limitations. Maybe you can re-evaluate if there's any better input method for what you're trying to do? Textarea doesn't seem to fit your requirements. – tfe Sep 13 '10 at 05:00
1

Maybe there is a way to get the "raw" number of "visual" lines. You should read the scrollHeight property of the textarea and divide it by the height of a line. Let's try.

Start with this HTML:

<textarea id="ta" cols="50" rows="10"></textarea>

Then:

var line_height = Math.floor($("#ta").height() / parseInt($("#ta").attr("rows")));
var dirty_number_of_lines = Math.ceil($("#ta")[0].scrollHeight / line_height);

I am not sure if that really works, just a mad theory.

Daniel O'Hara
  • 13,307
  • 3
  • 46
  • 68
  • line-height is a CSS attribute, you should be able to obtain it without resorting to math (the textbox height is not guaranteed to be a multiple of the line height, anyway). – Tgr Sep 13 '10 at 00:59
  • No, just `line-height` is not sufficient in this task. I cannot just take `line-height` and divide by it. I have to calculate the height of lines in the `textarea` manually because `font-size` may vary too. – Daniel O'Hara Sep 13 '10 at 06:15
0

You can calculate is as so:

var length = $('#textarea').val().split("\n").length;
HelpNeeder
  • 6,383
  • 24
  • 91
  • 155
-1

The number of characters allowed per line is dictated by the "cols" attribute of the textarea.

<textarea rows="10" cols="80"></textarea>

Assuming 80 characters per line, a good estimate may be:

var approxNumLines = textareaElement.value.length / textareaElement.cols ;

Doesn't account for word-break and word-wrap.

bobobobo
  • 64,917
  • 62
  • 258
  • 363