Ask your WordPress questions! Pay money and get answers fast! (more info)

limit the length of Wysiwyg content wp 3.9 ACF 5 WordPress

  • SOLVED

Hi,

I found the following post:
[[LINK href="http://www.wpquestions.com/question/showChrono/id/7520"]]http://www.wpquestions.com/question/showChrono/id/7520[[/LINK]]
and I need some help to adapt this for the following reasons:

apparently the onKeyDown.add, is depreciated so this wont work with Wordpress 3.9 and ACF 5:

I have been trying with

activeEditor.on('onKeyDown' function(ed,e) {
...
}


but it dosen't seem to work.

Here is what I am trying to achive:

I am using ACF Wysiwyg and have a few of them on the same post I store a max length value on the html of the field using so that i can reference to it later, and also I sotre an empty div with a class of total_count so I can inject on it's html the count of charters

function action_function_name( $field ) {
if($field['_name'] == 'txt'){
echo '<div class="total_count"> </div> | <div class="limitVal">'.100.'</div>';
echo 'Field name ' . $field['_name'];
}

}

add_filter( 'acf/render_field', 'action_function_name', 10, 1 );


When a key is pressed on the activeEditor I want to:

-Get in a var $edLength the text editor body (text) length.
-Get in a var the current acriveEdior ID
-Find the .closest(); div with the class "limitVal";
-IF the value of the $edLength is > then .closest(".limitVal").html
-prevent typing and display a message (an alert or changinf a class on a dom element)
-Else
-Find the .closest(); div with the class "total_count"
and inject the $edLength value in it's HTML.

I am realy stuck at the beginning of the process and doesen't have much time to try to solve this.

Any help much apreciated.


Answers (3)

2014-05-13

Dbranes answers:

I got this working for a single ACF WYSIWYG field, but I guess you can then expand it for the other fields as well.

One of the problem is to get the ever changing id attribute of ACF editor textarea.

It's possible to use for example:

jQuery('#acf-myeditor .acf_wysiwyg textarea').attr('id');


to grab it.


Here's the demo (see also the attached screenshot):

jQuery(document).ready( function( $ ) {

/**
* Our ACF setup:
*/
var myacf = {
data : {
editor_id: '',
max_length: 0,
error_msg: '',
div_selector: '',
textarea_selector: '',
counter_selector: '',
counter_error_selector: '',
counter_html: '',
},
init: function( max_length, div_selector, textarea_selector, counter_selector, counter_error_selector, error_msg, counter_html ){

// Get user input:
myacf.data.max_length = max_length;
myacf.data.div_selector = div_selector;
myacf.data.textarea_selector = textarea_selector;
myacf.data.counter_selector = counter_selector;
myacf.data.counter_error_selector = counter_error_selector;
myacf.data.error_msg = error_msg;
myacf.data.counter_html = counter_html;

// Check if our ACF WYSIWYG textarea exists and tinymce is available:
if( ! tinymce || ! $( myacf.data.textarea_selector ).length > 0 ){
console.log( 'Error: myacf not initialized!' );
return;
}

// Grab the ACF textarea id:
myacf.data.editor_id = $( myacf.data.textarea_selector ).attr('id');

// Add counter box:
$( myacf.data.div_selector ).append( myacf.data.counter_html );

tinymce.init({
setup: function (editor) {

//myacf.show_counter( myacf.get_length( myacf.data.editor_id ) );

if ( editor.id === myacf.data.editor_id ){

//----------------------
// Bind on KeyDown event
//----------------------
editor.on( 'keydown', function (e) {

// Dispaly counter:
var length = myacf.get_length( myacf.data.editor_id );
myacf.show_counter( length );

// Too many chars:
if( length + 1 > myacf.data.max_length ) {

// Let's allow the user to use backspace/delete when above the max char limit:
if( '46' != e.keyCode && '8' != e.keyCode ) {
e.preventDefault();
}
// Dispaly error:
myacf.show_error( myacf.data.error_msg );
}else{
// Clear error:
myacf.show_error( '' );
}
});

//----------------------
// Bind on KeyUp event:
//----------------------
editor.on( 'keyup', function (e) {
var length = myacf.get_length( myacf.data.editor_id );
myacf.show_counter( length );
});

}
}
});
},
get_length: function( id ){
return tinymce.get( id ).getContent().replace(/(<[a-zA-Z\/][^<>]*>|\[([^\]]+)\])|(\s+)|(\&nbsp;)/ig,'').length;
},
show_counter: function( length ){
$( myacf.data.counter_selector ).val( length + ' / ' + myacf.data.max_length );
},
show_error: function( msg ){
$( myacf.data.counter_error_selector ).html( msg );
}
}



/**
* Initialize:
*
* Example: myacf.init( max_length, div_selector, textarea_selector, counter_selector, counter_error_selector, error_msg, counter_html );
*
*/

myacf.init( 10,
'#acf-myeditor',
'#acf-myeditor .acf_wysiwyg textarea',
'#acf-myeditor .acf-counter',
'#acf-myeditor .acf-counter-error',
'Too many chars!',
'<div class="acf-counter-box"> Counter: <input type=\"text\" style=\"width:75px;\" value=\"0\" class=\"acf-counter\" readonly=\"\"> <span class=\"acf-counter-error\"></span></div>'
);

});


where you have to modify the correct selectors to your needs.

I added a <em>keyCode</em> check, to allow delete/backspace when the maximum limit is reached.

Additionally one should reset the counter on page load, but it's currently only set during a keyup/keydown event.

<strong>Update:</strong>

Here's an idea on how to support multiple ACF editors. It seems to work on my setup:

jQuery(document).ready( function( $ ) {

// Check if our ACF WYSIWYG textarea exists, tinymce is available or acfs is already defined:
if( typeof tinymce == 'undefined' || typeof acfs != 'undefined' || $( '.acf_wysiwyg textarea' ).length < 1 ){
console.log( 'Notice: Our acfs object is not initialized!' );
return;
}

var acfs = {};
acfs.editors = {};

//--------------------------------------------
// ACF editor settings (modify to your needs)
//--------------------------------------------

acfs.data = {}
acfs.data.error_msg = 'Too many chars!';
acfs.data.counter_html = '<div class="acf-counter-box"> Counter: <input type=\"text\" style=\"width:75px;\" value=\"0\" class=\"acf-counter\" readonly=\"\"> <span class=\"acf-counter-error\"></span></div>'

// Editor #1
var key = $( '#acf-myeditor1 .acf_wysiwyg textarea' ).attr( 'id' );
acfs.editors[key] = {};
acfs.editors[key].max_length = '10';
acfs.editors[key].div_selector = '#acf-myeditor1';
acfs.editors[key].counter_selector = '#acf-myeditor1 .acf-counter';
acfs.editors[key].counter_error_selector = '#acf-myeditor1 .acf-counter-error';

// Editor #2
var key = $( '#acf-myeditor2 .acf_wysiwyg textarea' ).attr( 'id' );
acfs.editors[key] = {};
acfs.editors[key].max_length = '5';
acfs.editors[key].div_selector = '#acf-myeditor2';
acfs.editors[key].counter_selector = '#acf-myeditor2.acf-counter';
acfs.editors[key].counter_error_selector = '#acf-myeditor2.acf-counter-error';

// Editor #3
// ...

//------------------
// Event handlers:
//------------------

// Key Down Event handler:
acfs.handle_keydown = function( e, maxlength, editorid ) {
// Display counter:
var length = acfs.get_length( editorid );
acfs.show_counter( acfs.editors[editorid].counter_selector, length, maxlength );

// Too many chars:
if( length + 1 > maxlength ) {
// Let's allow the user to use backspace/delete when above the max char limit:
if( '8' != e.keyCode && '46' != e.keyCode && 4 < e.keyCode ) {
e.preventDefault();
}
// Dispaly error:
acfs.show_error( acfs.editors[editorid].counter_error_selector, acfs.data.error_msg );
}else{
// Clear error:
acfs.show_error( acfs.editors[editorid].counter_error_selector, '' );
}

}

// Key Up Event handler:
acfs.handle_keyup = function( e, maxlength, editorid ) {
var length = acfs.get_length( editorid );
acfs.show_counter( acfs.editors[editorid].counter_selector, length, maxlength );
}

// Get number of chars of a given tinymce textarea
acfs.get_length = function( id ){
return tinymce.get( id ).getContent().replace(/(<[a-zA-Z\/][^<>]*>|\[([^\]]+)\])|(\s+)|(\&nbsp;)/ig,'').length;
}

// Show number of chars and max chars, of a given ACF textarea
acfs.show_counter = function( selector, length, max_length ){
$( selector ).val( length + ' / ' + max_length );
}

// Show too many chars error, for a given ACF textarea
acfs.show_error = function( selector, msg ){
$( selector ).html( msg );
}


//---------------------
// Connect to tinymce
//---------------------

tinymce.init({
setup: function (editor) {
if( typeof acfs.editors[editor.id] != 'undefined' ){

// Add counters box:
$( acfs.editors[editor.id].div_selector ).append( acfs.data.counter_html );

//----------------------
// Bind on KeyDown event:
//----------------------
editor.on( 'keydown', function (e) {
acfs.handle_keydown( e, acfs.editors[editor.id].max_length, editor.id );
});

//----------------------
// Bind on KeyUp event:
//----------------------
editor.on( 'keyup', function (e) {
acfs.handle_keyup( e, acfs.editors[editor.id].max_length, editor.id );
});
}
}
});


ps: Keep in mind that when you copy/paste code from this site, you get extra new lines.


l.pirondini comments:

Hi, thanks for the answer I'll try that. do I have to put that code inside my script registred with acf/input/admin_enqueue_scripts ?


Dbranes comments:

I didn't do that when testing, but you should try that.

I updated the code, but notice the current drawback of my demo ;-)


Dbranes comments:

I updated the answer with a keycode check, to allow backspace/delete when the maximum is reached.


l.pirondini comments:

Hi, Thanks a lot for the updates, I was about to strat implementation right now I'll let you know how it goes


Dbranes comments:

ok, it's getting late here, so I will check again tomorrow ;-)

ps: maybe one should allow arrow keys on maximum char limit.


l.pirondini comments:

Hi Dbranes,
Firs of all thanks for your greate job.

I tested your code on a single ACF Wysiwyg and il worked good, about implementing that for multiple ACF Wysiwyg on the same page (mainly inside Flexible content field so I was thinking targeting them like that
['data-layout="template1"'] #acf-myeditor
so for each template I can set different rules.

what is the best way to initiate the "myacf" object?

Should I do
$('['data-layout="template1"']').each(){
myacf.init( max_length, div_selector, textarea_selector, counter_selector, counter_error_selector, error_msg, counter_html );

}


that's not working as it will append on each div_selector the counter

Would this work better

i = 0;
$('['data-layout="template1"']').each(){
acf + i = new myacf;
acf + i .init( max_length, div_selector, textarea_selector, counter_selector, counter_error_selector, error_msg, counter_html );
i++;
}


so basicaly for each template i initiate a different "myacf" object.


Dbranes comments:

Hi l.pirondini

I updated the answer with an idea for multi ACF editors, that seems to work on my install.

2014-05-13

S├ębastien | French WordpressDesigner answers:

function(ed) {
ed.on("KeyDown", function(ed,evt) {
var content = tinyMCE.activeEditor.getContent();
var text_length = tinyMCE.activeEditor.getContent().replace(/(<([^>]+)>)/ig,"").length;
console.log ('text length' + text_length)
});
}

2014-05-13

Just Me answers:

Take a look at this tutorial http://joomjunk.co.uk/extras/tutorials/xhtml-tutorials/item/71-javascript-text-counter-characters-remaining.html


l.pirondini comments:

Hi, Thanks for the link, That is the exact behaviour I am looking for.
Maybe i could use that to target the Acf ".field_type-wysiwyg" and then the iframe inside it and finaly the <body> of that iframe, where the content is in html.

I'll give this a try