I use the following directive to enter editable content on a tag
I modified it to add a character counter.
But when I make line breaks, it counts me more characters.
in the image the counter displays 4 characters whereas visually there are only two.
here the <
transforms into 4 characters instead of one and not directly ">"
How to actually calculate the number of characters
Here is the directive used
directives.directive('contenteditable', ['$timeout', function($timeout) {
return {
restrict: 'A',
require: ['^?ngModel'],
link: function(scope, element, attrs, args) {
var ngModel = args[0];
if (ngModel === null) {
return null;
}
// console.log(element);
var modelKey = getModelKey();
opts = {
onlyText: false,
convertNewLines: false,
noLf: false,
};
angular.forEach(['onlyText', 'convertNewLines', 'noLf'], function(opt) {
if (attrs.hasOwnProperty(opt) && attrs[opt] && attrs[opt] !== 'false') {
opts[opt] = true;
}
});
$timeout(function() {
return (opts.onlyText && opts.noLf) ? element.text(ngModel.$modelValue) : element.html(ngModel.$modelValue);
});
var validate = function(content) {
var length = content.length;
refreshFn = function(content) {
if (content == undefined || content == null) {
content = '';
}
scope.maxCharacter = attrs.ngMaxlength;
scope.remaining = (length);
};
scope.$watch('ngModel', function(content) {
refreshFn(content);
}, true);
if (length > attrs.ngMaxlength) {
ngModel.$setValidity(modelKey, false);
return element.addClass('-error');
}
if (element.hasClass('-error')) {
ngModel.$setValidity(modelKey, true);
return element.removeClass('-error');
}
};
var read = function() {
var content = '';
if ((opts.onlyText && opts.noLf)) {
content = element.text();
} else {
content = element.html();
if (content) {
content = parseHtml(content);
}
}
if (content !== '') {
content = content.replace(/ /g, '');
content = content.trim();
}
ngModel.$setViewValue(content);
validate(content);
};
ngModel.$render = function() {
if ((opts.onlyText && opts.noLf)) {
element.text(ngModel.$viewValue || '');
} else {
element.html(ngModel.$viewValue || '');
}
};
element.bind('blur keyup change', function(event) {
scope.$apply(read);
scope.displayCount = true;
if (element.text().length >= attrs.ngMaxlength) {
event.preventDefault();
return false;
}
if (event.type === 'blur') {
scope.$apply(ngModel.$render);
}
});
function getModelKey() {
if (typeof attrs.ngModel === 'undefined') {
return null;
}
var split = attrs.ngModel.split('.');
return split[split.length - 1];
}
function parseHtml(html) {
html = html.replace(/ /g, '');
if (opts.convertNewLines || opts.noLf) {
var lf = '\r\n',
rxl = /\r\n$/;
if (opts.noLf) {
lf = ' ';
rxl = / $/;
}
html = html.replace(/<br(\s*)\/*>/ig, ''); // replace br for newlines
html = html.replace(/<[div>]+>/ig, lf); // replace div for newlines
html = html.replace(/<\/[div>]+>/gm, ''); // remove remaining divs
html = html.replace(/<[p>]+>/ig, lf); // replace p for newlines
html = html.replace(/<\/[p>]+>/gm, ''); // remove remaining p
html = html.replace(rxl, ''); // remove last newline
}
if (opts.onlyText) {
html = html.replace(/<\S[^><]*>/g, '');
}
return html;
}
}
};
}]);
Because a line feed has 2 characters in Windows and 1 character in Linux. The same way a space is a character altough visually is not a character, but an empty space, the line feed is another character.
"Visually there are only two" is false, as you can see a line feed, and that's a visual modification (the same way as a space, I repeat).
If you don't want to count line feeds, just remove them before getting the length
. There are two characters for linefeeds: \r
and \n
, so:
var length = content.replace(/\r|\n/g, "").length;
In the case that you want to remove all linefeeds, spaces and so on to just count the visual characters, then you just:
var length = content.replace(/\s+/g, "").length;