Reputation: 3235
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.
Upvotes: 52
Views: 72913
Reputation: 35
You can use a variable to equalize the current text and check if the text is longer than a value. If it is, do not equalize the target textContent to the variable.
var text = null
const textChangeHandler = (e) => {
if (e.currentTarget.textContent.length > 240) {
// do nothing
e.currentTarget.textContent = text
} else {
text = e.currentTarget.textContent
e.currentTarget.textContent = text
}
}
<p contentEditable={ "true"} style={{cursor: "text"}} onInput={textChangeHandler}>
</p>
Upvotes: 0
Reputation: 1
<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);
});
Upvotes: -1
Reputation: 39
This is another version of the answer of user113716. It supports the "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();
}
Upvotes: 3
Reputation: 28742
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>
Upvotes: 5
Reputation: 1303
This is the best way to do this in the most generalized form, and it just works 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;
}
});
Upvotes: 4
Reputation: 322592
Pass the event
object to your function and call e.preventDefault()
if the maximum 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'.
Also, you could probably get rid of the keyup
handler. keydown
should be enough.
Upvotes: 56
Reputation: 2136
This is a more generalized version of user113716's answer due to it not working appropriately when multiple contenteditable fields are using the target class. Their answer counts the total number of characters for all of the elements matching the given class, so you can only enter maximum 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();
}
}
Upvotes: 0
Reputation: 349
A simple way to achieve this:
<div onkeypress="return (this.innerText.length <= 256)" contenteditable="true">
Upvotes: 34
Reputation: 324707
Firstly, this kind of thing is irritating for the user: I would suggest instead doing something similar to Stack Overflow'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 Internet Explorer, 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.
Upvotes: 20
Reputation: 4819
$("[contenteditable=true]").keydown(function(e) {
var max = $(this).attr("maxlength");
if (e.which != 8 && $(this).text().length > max) {
e.preventDefault();
}
});
Upvotes: 1
Reputation: 1
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();
}
}
Upvotes: 0
Reputation: 14426
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;
}
});
Upvotes: 4
Reputation: 1423
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);
};
Upvotes: 0