3

I am looking for a method to limit input to a textarea in a way that enables user input to stay within a specified area WITHOUT unnecessary constraints to maximum number of characters.

The obvious method is maxlength, but that means maxlength will be determined by how much space an all caps input would take up, and that will require a maxlength that is unnecessarily low.

There are also various JS / jQuery solutions that limit the amount of lines possible to input in a text area (e.g. Limit number of lines in textarea and Display line count using jQuery), but these solutions, as far as I have been able to find, are dependent on the ENTER key, but doesn’t work if you copy-paste a piece of text into the text area.

To clarify

<!doctype html>
<head>
    <style>
    .one{
        height: 60px;
        width: 55px;
        background-color: pink;
        font-size: 16px;
    }
    </style>    
</head>
<body>
    <div class="one">i i i i i i i i i i i i i i i i i i</div>
    <br>
    <div class="one">M M M M M M M M M</div>
</body>
</html>

Div one can contain 18 lowercase “i” but only 9 capital “M”, so in order to ensure input would never exceed div one, the maxlength would have to be set to 9. The general result would be that the Div one area would generally not be fully used.

The above is merely a simple example to quickly explain which output I desire. The solution should ofc be tied to an input via a form textfield.

Ideas?

Cheers

Community
  • 1
  • 1
Rene Jorgensen
  • 169
  • 1
  • 8
  • Textareas generally display text in a monospaced font -- for good reason! A lowercase i and capital M should take up the same amount of space. You didn't change the font, did you? – wwwmarty Oct 01 '15 at 16:45
  • I didn't change the font no and using a monospaced font would of course be a simple way to solve it, but, ultimately, also unsatisfying :) – Rene Jorgensen Oct 01 '15 at 17:22
  • 2
    Ok well one trick I've seen is this. Onkeypress in the textarea, copy the value to a hidden (visibility:hidden) div (display:inline-block), and then checking the height and width of that div, to see if it exceeds your limitations. – wwwmarty Oct 01 '15 at 17:49

2 Answers2

2

I can't see this being an easy problem to solve. There is probably someone out there with more expertise than I, but the only way I can immediately think of to do this is to create a Map of characters and to assign them a width. Using this map, you could compare any new input against the map and determine how long/wide the input is.

Example pseudo code, this probably won't work as is;

   // YOUR CHAR MAP WITH WIDTHS ASSIGNED TO LETTERS
    var charMap = {
      i: 1,
      M: 2
    }

   // MAX AMOUNT OF CHARACTERS
   var charLimit = 18;

   // ASSUME THE TEXTAREA IS EMPTY, COULD GET ACTUAL WIDTH HERE INSTEAD
   var currentChars = 0;

   $('textarea').on('change', function{
     // GET ALL NEW INPUT
     var chars = $(this).val();
     // FOR LIMITING THE INPUT, THIS IS THE FINAL STRING THAT WILL END UP IN THE TEXTBOX
     var limitedUserInput = '';

     // CHECK EACH CHAR ONE BY ONE, COMPARING ITS WIDTH USING THE MAP
     for(var x = 0; x < chars.length; x++){
       currentChars += chars[x];
       // IF SIZE LIMIT NOT HIT, ADD CHARACTER TO OUTPUT STRING
       if(currentChars < charLimit){
         limitedUserInput += chars[x];
       }
     }

     // DISPLAY THE OUTPUT IN THE TEXT AREA (TRIMMING ANY EXTRA CONTENT)
     $(this).html = limitedUserInput;
   })

This would allow you to have 18 x charMap.i (18) or 9 x charMap.M (18).

I hope that makes some kind of sense.

Lewis
  • 3,479
  • 25
  • 40
  • 2
    I did consider doing that, but it seemed like a very cumbersome way to solve it, and you would probably have to redo it if you decided to change the output font. Thanks for giving it a bash anyway :) – Rene Jorgensen Oct 01 '15 at 17:25
  • 1
    @renejorgensen Fair enough, makes sense! I suppose you could first clone the text to a hidden span and use the span's width to determine whether the text would be too long for your input.? – Lewis Oct 01 '15 at 17:39
0

Here's my bid on a solution to the problem. There are a few bugs, but I'm quite satisfied with it.

I spend a lot of time trying to work something out by counting lines in the text area, but abandoned the effort as I came across increasingly complex solutions.

This solution depends on the div height and trims a .substring generated from the textarea user input so it fits within the desired div, in this case myDiv. The trimed string is also put into a second textarea which will be the one used in the form.

<!doctype html>
<head>
    <script src="https://code.jquery.com/jquery-1.10.2.js"></script>

   <style>
    .myDiv{
        min-height: 100px;
        width: 100px;
        background-color: pink;
        position: absolute;
    }
    #mySecondDiv{
        background-color: transparent;
        border: 1px solid black;
    };
    </style>

</head>
<body>

<textarea id='userInput' cols="30" rows="7"></textarea>
<textarea id="toBeStored"></textarea>

<div class='myDiv'></div>
<div class='myDiv'></div>
<div class='myDiv' id='mySecondDiv'></div>

<script>
//For live versions set the 'left' property for .myDiv(0) and #toBeStored to negative so they are off screen
$('.myDiv').eq(0).offset({ top: 200, left: 350 });
$('#toBeStored').offset({ top: 10, left: 350});

$('.myDiv').eq(1).offset({ top: 200, left: 10 });
$('.myDiv').eq(2).offset({ top: 200, left: 10 });

var currentMaxChars = 0;
var testString = "";
var myDivHeight = $('.myDiv').height()

$(document).ready(function(){
    while($('.myDiv').eq(0).height() <= myDivHeight ){
        testString += " i";
        $('.myDiv').eq(0).html(testString);
        currentMaxChars++;
    };
    currentMaxChars = currentMaxChars*2;
    maxChars = currentMaxChars;

    $("#userInput").on('change keyup paste', function() {
        var input = $(this).val().replace(/\n/g, '<br/>');
        $('.myDiv').eq(1).html(input);

        var str = $('#userInput').val().replace(/\n/g, '<br/>');
        if(str.length == currentMaxChars){
            currentMaxChars = maxChars;
        };

        if($('.myDiv').eq(0).height() <= myDivHeight){
            $('.myDiv').eq(0).html(str.substring(0, currentMaxChars));
            $('#toBeStored').html(str.substring(0, currentMaxChars));
        } else {
            while($('.myDiv').eq(0).height() > myDivHeight){
                currentMaxChars--;
                $('.myDiv').eq(0).html(str.substring(0, currentMaxChars));
                $('#toBeStored').html(str.substring(0, currentMaxChars));
            };
        };
    });
});
</script>
</body>
</html>
Rene Jorgensen
  • 169
  • 1
  • 8