Jump to content
bfncs

Char counter for text/textarea fields

Recommended Posts

Hi there,

I just recently discovered ProcessWire and am still pretty exited about it and the ways it enables you to do anything. Thanks a lot to Ryan and everything who created it!

What I was missing (or just overlooking?) recently, was some kind of char counter for textual input fields. Just an (en-/disable-able) are that shows, how many chars have been typed in. If you have some char limit defined for the field, it should show you, how many chars are left. Just think of the twitter online interface for writing new tweets.

So, did I miss something here and this is already existing? If not, I think that this would make a nice addition to the backend modules. I'd give a try to develop it, if it doesn't exist, but since I'm quite new to the PW world, I haven't ever developed a module before and it will probably take some time.

Please just let me know what you think of it.

With kind regards,

Marc

Edit:

Thanks so Soma, there is some Code there already. While still rough around the edges, it's definitely usable: https://github.com/boundaryfunctions/TextareaCounter

  • Like 4

Share this post


Link to post
Share on other sites

Hi, and welcome to the forums!

Not exactly what you are asking, but there is a TinyMCE plugin for wordcount. To enable it in any TinyMCE field go to the edit field "input settings", and in "TinyMCE configuration options" add "wordcount" to the plugins list and "theme : advanced" and "mode : textareas" in two different lines to "Aditional TinyMCE settings"

  • Like 4

Share this post


Link to post
Share on other sites

Hello diogo,

thanks a lot for pointing this out, as I wasn't aware of it! This will definitely be useful for a lot of content editing.

Still, what I was thinking about were more the text-only fields without any WYSIWYG editor or any tags. I was thinking about adding the charcounter for the meta description field, to give the content editor some immediate feedback on how much chars where used. It would also be great to have a word counter (and maybe a comma-seperated items counter).

Also, I usually like to give the content editors as few as possible opportunities to make something wrong, avoiding WYSIWYG as much as possible. So even parts of the content may be written as plain text in the backend and all converting to beautiful HTML (if any) is done in the template. For me this has lead to much less errors and more stable sites. Still I would like to give the editors feedback on char count (and even better optional/additional word count).

Because I'm still new to ProcessWire and module development for it in particular, maybe someone can give some pointers on what would be the best route to make a module for this functionality. What would be the best place to hook in to inject the JS? Can I add this to the standard text/textarea Inputfield modules without problems (which would be the more elegant solution imho) or would it be better to use the original modules as a starting point and make my own module from there (which should require a lot less work)?

With kind regards,

Marc

Share this post


Link to post
Share on other sites

You would have to build a module that extends InputfieldTextarea, limits the amount of characters, and that calls the necessary javascript counter.To know how to do this, have a look at these two examples on github AceEditor and TinyMCE. Notice how the configuration fields are created with ___getConfigInputfields() on both of them. You will need it at least to set the limit on a per field basis.

  • Like 1

Share this post


Link to post
Share on other sites

Wow, this sounds perfect, thanks a lot!

I'll definitely give it a try and will come back here, if it works out. Might take some time though...

Share this post


Link to post
Share on other sites

Nice idea, I was also thinking about some time ago.

One way is to not extend the InputfieldTextarea, and make a new Inputfield type, but Hook into the necessary methods to extend the config and rendering.

Since this made me wonder how it would go, I gave it a try. It's a nice example showing how to archive it using hooks only. It will give you a extra Input setting "Max length" in the field setup. And a counter for all InputfieldTextarea (not tinymce fields) and add a attribute "data-maxlength" to the one that have a max length value. I had to use "maxchars" as the setting field name as there's already this attribute, but not used in the context of page edit.

The script js is also added via a hook, and is a basic example of the script that counts backward while typing. Since I was already that far it was easy to add, but you might want to modify it and add features if you like.

This took me roughly 30min, and little more to add the js. I think a year ago I wouldn't knew how and would have taken ages to figure out. But it grows on you after so many modules and insight. This module shows you some good practices and maybe helps understanding modules. If you have questions please bring them in.

Module code: https://gist.github.com/4252958

JScript code: https://gist.github.com/4252962

Put them in a folder /site/modules/TextAreaCounter/..

Install, enjoy.

Edit: just found an issue... will take a look again :D

  • Like 8

Share this post


Link to post
Share on other sites

Soma, this is so great, thanks a lot for your good (and amazingly quick!) work!

This works like a charm and the best thing is, that you can literally read through the code and grasp anything that is done there. Not that I would have been able to do it like this, especially not in that amount of time. This really shows how much thought was put into the PW API.

Are you going to submit this to the official module repo?

There's are still possibilities for improvement, like but not limited to:

  • apply to flat textfields as well,
  • making it count words, and
  • preventing the user from typing more than allowed in a more drastic way.

With your foundation, I will definitely be able to implement this. By the way, did I thank you already?

  • Like 3

Share this post


Link to post
Share on other sites

Feel free to take it and improve it. Make it your own whatever and be sure to submit it to modulers page. You're welcome. Glad if it helps people.

  • Like 4

Share this post


Link to post
Share on other sites

Hi,

just wanted to let you know, that I started another gist for this here:

https://gist.github.com/4258927

I added the word count thingy and some smaller tweaks, but I'm definitely going to add the rest (particularly proper documentation), when I find time for it.

Thanks, Soma, for kickstarting me on this!

Ah, and another question for everyone:

Would it be more elegant to integrate the functionality for plain text inputfields in this module or would you prefer to have it as a seperate one?

  • Like 4

Share this post


Link to post
Share on other sites

Greetings,

Well, what do you know. I was wondering about a counter for my textarea fields, and there it is.

THANK YOU SOMA FOR CREATING THIS!

I've not only installed it, but studied it. I like how clean this code is and how easy it is to understand what it does. That says a lot about Soma's ability to create modules, and also about ProcessWire's module-building system.

Has this been entered into the official ProcessWire module listing?

Thanks again,

Matthew

EDIT: Thank you boundaryfunctions for running further with this! I can do some documentation for this, if you need me to.

  • Like 1

Share this post


Link to post
Share on other sites

Hey Matthew,

you're welcome. Thanks for the feedback and you're right, most oft the credit goes to Soma. This isn't in the Moduls repo for now because I wanted to get some things right in the first place, but I hope it's going there soon.

I did some searching but didn't really solve this problem, so I hope someone can give me a pointer on this: where would I hook to do some custom validation for my input field (including error reporting)? Any help is very much appreciated.

  • Like 1

Share this post


Link to post
Share on other sites

Hello boundaryfunctions,

I installed your Github version (extension?) and it works great.

This is a terrific addition to the text and textarea fields. The only thing left is to figure out how to make it so a user cannot enter more than the character limit. I'm thinking a good way to do this is by not allowing any more characters to be entered once the limit is reached (rather than a validation error).

These snippets seem to have elements that can work for us:

http://www.yourinspi...ide-a-textarea/

http://unwrongest.com/projects/limit/

http://stackoverflow...rea-with-jquery

http://www.devcurry....n-textarea.html

http://www.scriptiny...xtarea-limiter/

http://web.enavu.com/daily-tip/maxlength-for-textarea-with-jquery/

This is definitely also a good way to get into PW module creation...

Thanks,

Matthew

Share this post


Link to post
Share on other sites

Hi Matthew,

thanks for testing and doing research! :)

The only thing left is to figure out how to make it so a user cannot enter more than the character limit. I'm thinking a good way to do this is by not allowing any more characters to be entered once the limit is reached (rather than a validation error).

I'll definitely add some means of limiting the input clientside, but I also want to validate this clientside. I know, that the inputfields should have to deal with thrusted content only, but still, I think it is a question of good coding style to validate everything serverside as well, just to be really sure.

I looked into other modules to see how they do it, and it looked to me, that all of them do their "validation" in Fieldtype::sanitizeValue() (apigen) and without throwing any errors. Is this really the proposed way to do this? This seems really strange to me, why is there no Fieldtype::validate()?

Even if this is the way to go: Fieldtype:sanitizeValue() isn't hookable, so how could I slip my own code into FieldtypeTextarea then?

Can someone please shed some light on this?

Edit: Since I think answers to these questions might prove helpful to others as well, I opened a new thread in the Module Development Forum, asking the question a little more generalized. You can find it here:

http://processwire.com/talk/topic/2365-validation-for-own-inputfieldfieldtype-modules/

  • Like 1

Share this post


Link to post
Share on other sites

Hi boundaryfunctions,

This topic has me thinking of validation in general.

Validation is such a crucial part of any CMS, but I wonder whether there are better practices than what has always been done before? For example, can we get away from the "receive-an-error-upon-form-submission" scenario? Using JQuery or HTML input restrictors might be better in many cases. With text/textarea character limits, I feel like an immediate response in the field (not allowing you to type more characters) is better than waiting until you submit the form. That way, you prevent situations where someone composes a long entry only to find out later that it must be cut.

I'm looking at your code and Soma's code and trying to figure out how to use the JS to disallow further characters after a certain point. As I said, I'm looking at this as a way to get a useful module, and also to document how to do modules in ProcessWire.

Thanks,

Matthew

Share this post


Link to post
Share on other sites

Hey Matthew,

I'm totally with you about the immediate-response-thing, that's absolutely something that has to be there. You already posted some links to jQuery plugins that do this yourself, they all work basically the same way: on every interaction with the input, check whether the limit was reached and if it was, cut away all characters beyond the limit. Since the rise of HTML5, there's the maxlength attribute, too.

While this is all nice and therefore should and will be used, client-side validation can never substitute server-side validation.

I know, that in the normal use scenario, you only have accredited users on the system that don't want to do anything nasty with it, but still:

  • you may always have a Javascript error somewhere else that prevents your validation code from execution,
  • Javascript may be switched off,
  • the maxlength attribute isn't supported in older browsers,
  • everything that can possibly go wrong will go wrong,
  • everything that can't go wrong will almost certainly go wrong, and
  • it would be nice if this would module could be used safely in all kinds of frontend forms, too (because it probably will the sooner or the later).

All of these are imho good reason to do server side validation, even if you don't expect the evil user.

  • Like 1

Share this post


Link to post
Share on other sites

Hi boundaryfunctions,

We are totally in alignment. We need...

1. The immediate-response thing for stopping innocent people from wriitng a novel only to find out afterwards that we only want 100 characters

2. The server-side validation to make sure the 100 characters are not evil

Just for the moment, I'm trying to integrate number 1 into the plugin under discussion.

I also leave open the possibility that PW has validation elements already available, and I just am not yet using them.

Thanks,

Matthew

  • Like 1

Share this post


Link to post
Share on other sites

Ah ok, I somehow got you wrong in that you wanted to point out that server-side validation wouldn't be necessary, please don't mind! ;)

Your work on No. 1 is very much appreciated. Maybe your or I or someone else should put it in a complete github repository...

Share this post


Link to post
Share on other sites

Hey boundaryfunctions,

As soon as I get it working properly I will definitely add it!

I'm trying to do two things:

1. Cut the entry off after the maximum characters

2. Make the "characters left" turn red when the user gets to < 10 characters remaining

The first one is really required. The second one is just a nicde touch, but again this is all about getting faster with ProcessWire as well as making a functional module.

Thanks,

Matthew

  • Like 1

Share this post


Link to post
Share on other sites

Hi everyone,

I just worked on this a little and added client- and server-side validation. It would be great to get some feedback on this, so if anyone has some time to do testing, please check it out here:

https://github.com/boundaryfunctions/TextareaCounter

If there are no crucial bugs coming up for you, I'm going to submit this to the module repo soon.

  • Like 1

Share this post


Link to post
Share on other sites

Hello Boundaryfunctions,

I replaced the old one with this, and a look at the code tells me this is definitely heading in the right direction!

Here's what I'm seeing:

1. When I enter text, the "Characters left:" counter does not update as I'm typing

2. If I type some text, then hit "save," the "Characters left:" number updates properly

3. If too many characters are entered, it deletes everything that was entered in that "session"

Let me know if I can be of any further help with this. I'm looking at your code and trying to get a handle on all that this is doing.

Thank you for your excellent work.

Matthew

Share this post


Link to post
Share on other sites

Hi Matthew,

thanks a lot for your quick feedback, this is very much appreciated!

Strange, so it looks like the JS isn't loaded/doesn't work, but it certainly does for me. How did you update the code? The name changed from "TextAreaCounter" to "TextareaCounter", so the folder has to be renamed to "TextareaCounter", too, just a quick guess that it might have to do with this.

If this doesn't help: can you check whether TextareaCounter.js is loaded and whether there are any JS exceptions in your browsers' console? If you find any error messages there, please just paste them in here.

With kind regards,

Marc

Share this post


Link to post
Share on other sites

Hello,

I pasted the new code over the old code, then realized it wasn't working in the back end. Then I tried to delete the old module from the back-end, but it was no longer recognized (i.e., it was listed in the modules but not actually functional). The admin message I see is this:

Can't uninstall module - Module is not already installed

I then deleted the folder on my server and re-created the folder with the new name, uploaded the new files, then looked for "new" modules with PW back end. It did not find it, but the "old" module was still listed.

So, I guess I have to try to clean this up first!

When I try to delete the module now, I keep getting the same "Can't uninstall" message.

No JS errors, and the "TextareaCounter.js" file is being loaded.

Thanks,

Matthew

EDIT: I successfully stopped the module from appearing in the module list by deleting it again from the server. But after re-creating the directory again and re-uploading the files, then asking to "find new modules" it appears in the modules list automatically without adding it. But if I click on the module name, then "submit" it, I get this error:

Unable to find ID for Module 'TextareaCounter'

This might have nothing to do with the module! But I'm reporting it. Trying to fix it now.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By John Bates
      Which is better Elm or Typescript?
    • By schwarzdesign
      We recently rebuilt the Architekturführer Köln (architectural guide Cologne) as a mobile-first JavaScript web app, powered by VueJS in the frontend and ProcessWire in the backend. Concept, design and implementation by schwarzdesign!
      The Architekturführer Köln is a guidebook and now a web application about architectural highlights in Cologne, Germany. It contains detailled information about around 100 objects (architectural landmarks) in Cologne. The web app offers multiple ways to search through all available objects, including:
      An interactive live map A list of object near the user's location Filtering based on architect, district and category Favourites saved by the user The frontend is written entirely in JavaScript, with the data coming from a ProcessWire-powered API-first backend.
      Frontend
      The app is built with the Vue framework and compiled with Webpack 4. As a learning exercise and for greater customizability we opted to not use Vue CLI, and instead wrote our own Webpack config with individually defined dependencies.
      The site is a SPA (Single Page Application), which means all internal links are intercepted by the Vue app and the corresponding routes (pages) are generated by the framework directly in the browser, using data retrieved from the API. It's also a PWA (Progressive Web App), the main feature of which is that you can install it to your home screen on your phone and launch it from there like a regular app. It also includes a service worker which catches requests to the API and returns cached responses when the network is not available. The Architekturführer is supposed to be taken with you on a walk through the city, and will keep working even if you are completely offline.
      Notable mentions from the tech stack:
      Vue Vue Router for the SPA functionality VueX for state management and storage / caching of the data returned through the API Leaflet (with Mapbox tiles) for the interactive maps Webpack 4 for compilation of the app into a single distributable Babel for transpilation of ES6+ SASS & PostCSS with Autoprefixer as a convenience for SASS in SFCs Google Workbox to generate the service worker instead of writing lots of boilerplate code Bootstrap 4 is barely used here, but we still included it's reboot and grid system Backend
      The ProcessWire backend is API-only, there are no server-side rendered templates, which means the only PHP template is the one used for the API. For this API, we used a single content type (template) with a couple of pre-defined endpoints (url segments); most importantly we built entdpoints to get a list of all objects (either including the full data, or only the data necessary to show teaser tiles), as well as individual objects and taxonomies. The API template which acts as a controller contains all the necessary switches and selectors to serve the correct response in <100 lines of code.
      Since we wanted some flexibility regarding the format in which different fields were transmitted over the api, we wrote a function to extract arbitrary page fields from ProcessWire pages and return them as serializable standard objects. There's also a function that takes a Pageimage object, creates multiple variants in different sizes and returns an object containing their base path and an array of variants (identified by their basename and width). We use that one to generate responsive images in the frontend. Check out the code for both functions in this gist.
      We used native ProcessWire data wherever possible, so as to not duplicate that work in the frontend app. For example:
      Page names from the backend translate to URLs in the frontend in the form of route parameters for the Vue Router Page IDs from ProcessWire are included in the API responses, we use those to identify objects across the app, for example to store the user's favourites, and as render keys for object lists Taxonomies have their own API endpoints, and objects contain their taxonomies only as IDs (in the same way ProcessWire uses Page References) Finally, the raw JSON data is cached using the cache API and this handy trick by @LostKobrakai to store raw JSON strings over the cache API.
      Screenshots














    • By Brian Scramlin
      I just wanted to share that I added an AJAX-powered gallery to an artist website that I developed and host: https://jackpinecreations.com/gallery/

      There were two things that frustrated me about creating this. Perhaps you can show me a better way.
      1. After creating my processing script, which I placed under /templates/scripts/get-items.php, I realized that I would get a 403, due to ProcessWire's routing and security. This forced me to have to create a template and page for this little script. This was frustrating simply because it seemed unnecessarily confusing. But worse, see #2.
      2. I usually use config.php to prepend and append each of my templates with a head.inc and foot.inc, which keeps my templates easy to use and I don't have to go and use the GUI to do so on each template separately. However, since I realized I needed to create a new template and page so as to access it, whenever I sent POST params to it, I would get the header and footer along with it!!! I could find no workarounds and had to remove the pre/append calls in config.php and use the GUI on each template individually.  
      Code Below if you're interested:
      HTML and JavaScript (forgive my sad JavaScript skills, I know this can be tightened up)
      <!-- Begin Grid --> <div class="container mt-4"> <div id="gallery" class="row"> <?php foreach ($page->children("limit=9") as $child): ?> <div class="col-6 col-md-4 gallery-item"> <a href="<?= $child->url ?>" title="View <?= $child->title ?>"> <img class="gallery-item" src="<?= $child->item_featured_image->size(640, 640)->url ?>" alt="<?= $child->title ?> Image"> </a> </div> <?php endforeach; ?> </div> </div> <!-- End Grid --> <div class="center-block text-center"> <button id="get-more-items" type="button" name="get-more-items" class="btn-vintage">Load More</button> </div> <script type="text/javascript"> var buttonGetItems = document.getElementById("get-more-items"); var indexStart = 0; buttonGetItems.addEventListener("click", function() { indexStart += 9; $.ajax({ url: '<?= $pages->get(1186)->url ?>', type: "POST", dataType:'json', // add json datatype to get json data: ({page_id: <?= $page->id ?>, index_start: indexStart}), success: function(data){ console.log(data); if (data[1]) { //for each element, append it. $.each(data, function(key, value) { $("#gallery").append(value); }); } else { $("#get-more-items").after('<p class="center-block text-center">There are no more items to load.</p>'); $("#get-more-items").remove(); } } }); }); </script> Processing Script
      <?php $items_array = []; $i = 0; foreach ($pages->get($input->post->page_id)->children->slice($input->post->index_start, 9) as $child) { $i++; $items_array[$i] = "<div class='col-6 col-md-4 gallery-item'> <a href='$child->url' title='View $child->title'> <img src='{$child->item_featured_image->size(640,640)->url}' alt='$child->title Image'> </a> </div>"; } echo json_encode($items_array); I love ProcessWire for hundreds of reasons, but I've been using AJAX more and more, and I'm not liking having to create templates to access scripts. 
      Any advice?
    • By Sergio
      Confession bear meme on tests: I'm a virgin.
      Never implemented any of them, mostly because I work alone for many years now. But found this cool project today, called Cypress. 
      This is the easiest way to test a website or app I've found. Check their intro video out: https://docs.cypress.io/guides/getting-started/writing-your-first-test.html
      Note: I recommend this Chrome extension to speed up videos: https://github.com/igrigorik/videospeed as the narration of this video is kinda slow. 🙂
       
    • By Gadgetto
      Hi,
      for my GroupMailer module I've created a custom Fieldtype + Inputfield module which provides multi-column field values. The first field column is a visible text field and there are some other columns which are not presented to user (they are rendered as hidden form fields).

      This is the database schema:
      $schema['data'] = 'text NOT NULL'; // we're using 'data' to represent our 'subject' field $schema['sendstatus'] = 'tinyint NOT NULL DEFAULT 0'; // message send status $schema['recipients'] = "int(10) unsigned NOT NULL DEFAULT 0"; // recipients counter $schema['sent'] = "int(10) unsigned NOT NULL DEFAULT 0"; // sent counter $schema['started'] = "int(10) unsigned NOT NULL DEFAULT 0"; // message sending start $schema['finished'] = "int(10) unsigned NOT NULL DEFAULT 0"; // message sending finished This are the ___wakeupValue and ___sleepValue methods:
      Now I try to extend this Fieldtype/Inputfield to provide multi language features.
      Only the first value ("data" which represents the "subject" field) should be/needs to be multi language!
      I had a look at the built in Fieldtypes (e.g FieldtypeText & FieldtypeTextLanguage) which provides multi language support but I couldn't find a similar case (multi-value field with language support). All built in Fieldtypes are single-value fields.
      I know this is a very "general" question but maybe somebody could push me in the right direction?
×
×
  • Create New...