Limiting Number of Characters in a ContentEditable div

I am using contenteditable div elements in my web application and I am trying to come up with a solution to limit the amount of characters allowed in the area, and once the limit is hit, attempting to enter characters simply does nothing. This is what I have so far:

var content_id = 'editable_div';

//binding keyup/down events on the contenteditable div
$('#'+content_id).keyup(function(){ check_charcount(content_id, max); });
$('#'+content_id).keydown(function(){ check_charcount(content_id, max); });

function check_charcount(content_id, max)
{   
    if($('#'+content_id).text().length > max)
    {
        $('#'+content_id).text($('#'+content_id).text().substring(0, max));
    }
}

This DOES limit the number of characters to the number specified by 'max', however once the area's text is set by the jquery .text() function the cursor resets itself to the beginning of the area. So if the user keeps on typing, the newly entered characters will be inserted at the beginning of the text and the last character of the text will be removed. So really, I just need some way to keep the cursor at the end of the contenteditable area's text.

Answers:

Answer

How about passing in the event object to your function and calling e.preventDefault() if the max is reached?

var content_id = 'editable_div';  

max = 10;

//binding keyup/down events on the contenteditable div
$('#'+content_id).keyup(function(e){ check_charcount(content_id, max, e); });
$('#'+content_id).keydown(function(e){ check_charcount(content_id, max, e); });

function check_charcount(content_id, max, e)
{   
    if(e.which != 8 && $('#'+content_id).text().length > max)
    {
       // $('#'+content_id).text($('#'+content_id).text().substring(0, max));
       e.preventDefault();
    }
}

Although, you may need to do a little more to allow the user to do things like 'delete'.


EDIT:

Added support for 'delete' key.


EDIT 2:

Also, you could probably get rid of the keyup handler. keydown should be enough.

Answer

A simple way to achieve this :

<div onkeypress="return (this.innerText.length <= 256)" contenteditable="true">
Answer

Firstly, this kind of thing is irritating for the user: I would suggest instead doing something similar to StackOverflow's comment field, which allows you type as much or as little as you like, shows you a message telling you how many characters you've typed and whether it's too many or too few, and refuses to let you submit a comment whose length is not valid.

Secondly, if you really have to limit the length of text, replacing the whole content of the <div> on every keystroke if the content is too long is unnecessarily expensive, and will make the editor unresponsive on slower machines. I suggest handling the keypress event and simply preventing the character being inserted using preventDefault() on the event (or in IE, setting the event's returnValue to true, assuming you're using attachEvent). This won't prevent the user from pasting text in, so you'll need to handle the paste event (which doesn't exist in Opera or Firefox < 3, so you'll need some kind of polling-based solution for those). Since you won't be able to access the content being pasted in advance, you'll have no way of knowing if the paste will take you over the character limit, so you'll need to set a timer to check the length again shortly after the paste. All that being the case, the first option seems preferable to me.

Answer

This is the best way to do this most generalize form and just work great for me!

<div contenteditable="true" name="choice1" class="textfield" max="255"></div>
    $('.textfield').on("keypress paste", function (e) {
        if (this.innerHTML.length >= this.getAttribute("max")) {
            e.preventDefault();
            return false;
        }
    });
Answer
$("[contenteditable=true]").keydown(function(e) {
    var max = $(this).attr("maxlength");
    if (e.which != 8 && $(this).text().length > max) {
        e.preventDefault();
    }
});
Answer

Another version of the answer of @user113716. Supports "paste" event, and some text hotkeys like CTRL + A.

$('#'+content_id).on('keydown paste', function (e) { maxLimitForContenteditableDiv(e, 140) });

function maxLimitForContenteditableDiv(e, limit) {
    var allowedKeys = false;

    if (e.type === 'keydown') {
        allowedKeys = (
            e.which === 8 ||  /* BACKSPACE */
            e.which === 35 || /* END */
            e.which === 36 || /* HOME */
            e.which === 37 || /* LEFT */
            e.which === 38 || /* UP */
            e.which === 39 || /* RIGHT*/
            e.which === 40 || /* DOWN */
            e.which === 46 || /* DEL*/
            e.ctrlKey === true && e.which === 65 || /* CTRL + A */
            e.ctrlKey === true && e.which === 88 || /* CTRL + X */
            e.ctrlKey === true && e.which === 67 || /* CTRL + C */
            e.ctrlKey === true && e.which === 86 || /* CTRL + V */
            e.ctrlKey === true && e.which === 90    /* CTRL + Z */
        )
    }

    if (e.type === 'paste') {
        setTimeout(function () {
            $(e.target).text($(e.target).text().slice(0, limit));
        });
    }

    if (!allowedKeys && $(e.target).text().length >= limit) {
        e.preventDefault();
    }

Answer
var onKeyPress = function () {
    var keyCode = window.event.keyCode,
        isNumeric = (keyCode > 47 && keyCode < 58),
        isNotEnterKey = !!(window.event.keyCode !== 13);

        (isNotEnterKey) || (this.blur());

        return (isNumeric && this.innerText.length < 3);
};
Answer

A more generalized revision of @user113716's answer which does not work for multiple contenteditable fields (it counts the total number of characters for all of the elements matching the given id, so you can only enter max characters total on the page).

This solution allows you to use a general class, and limits the number of characters in each contenteditable field independently.

the html:

<div contenteditable="true" name="choice1" class="textfield"></div>

And the JavaScript:

MAX_TEXTINPUT = 10;
TEXTFIELD_CLASS = "textfield"

$(document).ready(function() {

    //binding keyup/down events on the contenteditable div
    $('.'+TEXTFIELD_CLASS).keydown(function(e){ check_charcount(TEXTFIELD_CLASS, MAX_TEXTINPUT, e); });

})

function check_charcount(inputclass, max, e) {   
    var focused = $(document.activeElement)

    if(focused.hasClass(inputclass) && e.which != 8 && $(focused).text().length >= max)
    {
        e.preventDefault();
    }
}
Answer

The following solution also considers the control keys (which correspond to non-printable characters).

//Maximum number of characters
var max = 200;

$('#editable_div').keydown(function(e) {
    var keycode = e.keyCode;

    //List of keycodes of printable characters from:
    //http://stackoverflow.com/questions/12467240/determine-if-javascript-e-keycode-is-a-printable-non-control-character
    var printable = 
        (keycode > 47 && keycode < 58)   || // number keys
        keycode == 32 || keycode == 13   || // spacebar & return key(s) (if you want to allow carriage returns)
        (keycode > 64 && keycode < 91)   || // letter keys
        (keycode > 95 && keycode < 112)  || // numpad keys
        (keycode > 185 && keycode < 193) || // ;=,-./` (in order)
        (keycode > 218 && keycode < 223);   // [\]' (in order)

    if (printable) {
        //Based on the Bala Velayutham's answer
        return $(this).text().length <= max; 
    }
});
Answer

This is the way I did it by jQuery binding, making it easy for me by just adding a property data-input-length to a content editable element. Just add the javascript code anywhere in your document.

$(document).ready(function(){
	// Excempt keys(arrows, del, backspace, home, end);
	var excempt = [37,38,39,40,46,8,36,35];
	// Loop through every editiable thing
	$("[contenteditable='true']").each(function(index,elem) {
	    var $elem = $(elem);
	    // Check for a property called data-input-length="value" (<div contenteditiable="true" data-input-length="100">)
	    var length = $elem.data('input-length');
	    // Validation of value
	    if(!isNaN(length)) {
	    	// Register keydown handler
	        $elem.on('keydown',function(evt){
	        	// If the key isn't excempt AND the text is longer than length stop the action.
	            if(excempt.indexOf(evt.which) === -1 && $elem.text().length > length) {
	               evt.preventDefault();
	               return false;
	            }
	        });
	    }
	});
});
div {
  background-color:#eee;
  border: 1px solid black;
  margin:5px;
  width:300px;
  height:100px;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div contenteditable="true" data-input-length="100">
You can type a 100 characters here
</div>
<div contenteditable="true" data-input-length="150">
You can type a 150 characters here
</div>
<div contenteditable="true" data-input-length="10">
You can type a 10 characters here
</div>

Answer

If you would like to make it work with classes. In other words, element would be able to share class names and still have their own unique count:

var content_id = '.myclass';  

max = 1;
//binding keyup/down events on the contenteditable div
$(content_id).keyup(function(e){ check_charcount(this, max, e); });
$(content_id).keydown(function(e){ check_charcount(this, max, e); });

function check_charcount(elem, max, e){   
    if(e.which != 8 && $(elem).text().length >= max){       
        e.preventDefault();
    }
}
Answer
<div id="test" contenteditable="true" tabIndex="-1"></div>
  <p><span id="sy">0</span>/120</p> 
var flag = true;
 $('#test').on('compositionstart', function() {
        flag = false;
    });
    $('#test').on('compositionend', function() {
        flag = true;
    });
    $('#test').on('input', function() {
      setTimeout(function() {
        if (flag) {
          if($('#test').text().length>= 120) {
            setTimeout(() => {
              $('#test').text($('#test').text().substring(0, 120));
              var range = window.getSelection(); //??range
              range.selectAllChildren(document.getElementById('test')); //range ??obj??????
              range.collapseToEnd();
              $("#sy").text($('#test').text().length)
            }, 0);
          } else {
            $("#sy").text($('#test').text().length)
          }

        }
      }, 0);
    });

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.