JellyBelly
JellyBelly

Reputation: 2431

Zend Form TextArea with countdown characters and StringLength Validator - different calc

In my form i have a zend_form_element_textarea. For this element i add a validator StringLength like this:

$this->addElement('textarea', 'text', array('label' => 'F_MESSAGE_SMS_TEXT', 'required' => true, 'class' => 'fieldLiveLimit', 'limit' => 160));
$this->text->addValidator('StringLength', false, array('max' => 160));

and i use a script javascript for show live countdown characters:

//Text field or text area limit - get limit by field parameter
    $(".fieldLiveLimit").each(function () {
        var characters = $(this).attr('limit');
        var remaining = calcDifference(characters, $(this).val());
        if ($('.limitCounter').length > 0) {
            $(this).after($('.limitCounter').first().clone());
            $(this).next('.limitCounter').children('span').html(remaining);
        } else {
            $(this).after($("<div class='limitCounter'>" + translate('L_LIMIT_COUNTER', [remaining]) + "</div>"));
        }
        checkClassCounter(remaining, $(this));

        $(this).bind('textchange', function (event, previousText) {
            remaining = calcDifference(characters, $(this).val());
            checkClassCounter(remaining, $(this));
            if ($(this).val().length > characters) {
                $(this).val($(this).val().substr(0, characters));
            } else {
                $(this).next('.limitCounter').children('span').html(remaining);
            }
        });

        function calcDifference(characters, value) {
            return characters - parseInt(value.length);
        }

        function checkClassCounter(remaining, element) {
            if (parseInt(element.val().length) == 0) {
                element.next(".limitCounter").hide();
            } else {
                element.next(".limitCounter").show();
                if (remaining <= 10) {
                    element.next(".limitCounter").addClass('red-message');
                } else {
                    element.next(".limitCounter").removeClass('red-message');
                }
            }
        }
    });

this works well, except for one thing. If inside the text area there are the new lines, the validator zend the new line it counts as two characters, while my JS script as one. who is wrong? I think the zend validator, but it seems really strange as a thing and then ask to you!

Upvotes: 1

Views: 1123

Answers (1)

Moe
Moe

Reputation: 2730

It has to do with the line breaks, as user Pankrates already pointed out in his comment.

In fact, this problem is a lot more complex than it seems at first, because it has at least two dimensions:

  1. jQuery strips the carriage return character in the val() function: "At present, using .val() on textarea elements strips carriage return characters from the browser-reported value." jQuery documentation.
  2. It is inconsistent along browsers how line breaks with \r\n are counted. See here or here for related questions on SO. However, on all browsers I have installed on my system (Firefox 20.0 and Chrome 26.0), \r\n are counted as two characters, so I cannot confirm this.

See this little code snippet for a demonstration:

<?php
$str1 =  "test\nstring";
$str2 =  "test\r\nstring";
?>
    <textarea id="text1"><?php echo $str1 ?></textarea>jQuery: <span id="jquery1"></span>, JS: <span id="js1"></span>, PHP: <?php echo iconv_strlen($str1) ?>
    <textarea id="text2"><?php echo $str2 ?></textarea>jQuery: <span id="jquery2"></span>, JS: <span id="js2"></span>, PHP: <?php echo iconv_strlen($str2) ?>

<script type="text/javascript">
        $(document).ready(function() {
                $("#jquery1").text($("#text1").val().length);
                $("#js1").text("<?php echo str_replace(array("\n", "\r"), array('\n', '\r'), $str1) ?>".length);
                $("#jquery2").text($("#text2").val().length);
                $("#js2").text("<?php echo str_replace(array("\n", "\r"), array('\n', '\r'), $str2) ?>".length);
        });     
</script>

For the first box it gives me jQuery: 11, JS: 11, PHP: 11, but for the second box I get jQuery: 11, JS: 12, PHP: 12.

There are several solutions I can think of (none of which is ideal):

  1. Use a Zend_Filter_PregReplace in your form to replace all \r\n with \n. Pro: Counting will be consistent with that of jQuery's val() and relatively easy. Con: You are destroying the user's line break which might lead to unwanted results.
  2. Decorate the Zend_Validate_StringLength so that you can replace \r\n by \n in the isValid() method. Pro: Will preserve the user's line break, Con: You might get a valid result that is longer then 200 characters, because \r\n is counted as one character and you need to introduce a new class.
  3. Use the jQuery's textarea.valHooks to replace all line breaks with \r\n: Pro: Simple, Con: If you have users that have \n as line break char, it again will give you inconsistent results.

I hope this answer show you some directions on how you can tackle this situation depending on your app's context.

Upvotes: 1

Related Questions