" MicromOne: How to Add a Character Counter to a Text Field in Microsoft Dynamics 365 CRM Using JavaScript

Pagine

How to Add a Character Counter to a Text Field in Microsoft Dynamics 365 CRM Using JavaScript

 When customizing Microsoft Dynamics 365 Customer Engagement, small user interface improvements can make a big difference in the overall user experience.

One common requirement is to display a character counter for a text field. This feature allows users to see how many characters they have entered and how many are still available before reaching the maximum length.

In this article, we will explore how to implement a dynamic character counter for the Title field (dps_title) in Microsoft Dynamics 365 CRM using JavaScript.



Why Add a Character Counter?

In Dynamics 365 CRM, text fields often have a maximum length. Although the platform enforces this limit, users may not always know how many characters they have entered while typing.

A character counter provides immediate feedback and helps users:

  • Avoid exceeding the maximum character limit.

  • Write more concise and accurate titles.

  • Improve data quality and consistency.

  • Enjoy a better user experience when completing CRM forms.

For example, if the maximum length is 100 characters and the user has entered 35 characters, the counter displays:

35/100

The counter updates dynamically whenever the user changes the field value.

Understanding the Implementation

The solution uses JavaScript to locate the HTML input associated with the dps_title field, create a counter element, and update it whenever the user types.

The implementation consists of three main steps:

  1. Initialize the character counter when the CRM form loads.

  2. Locate the title input element in the form's document.

  3. Update the counter whenever the input value changes.

Step 1: Initialize the Character Counter

First, we define a function called InitTitleCharCounter.

_self.InitTitleCharCounter = function () {
    var dataId = "dps_title.fieldControl-text-box-text";

    var interval = setInterval(function () {
        var parentDoc = window.parent.document;

        var input = parentDoc.querySelector(
            '[data-id="' + dataId + '"]'
        );

        if (!input) {
            return;
        }

        clearInterval(interval);

        if (input.dataset.charCounterInitialized === "true") {
            return;
        }

        input.dataset.charCounterInitialized = "true";

        var maxLength =
            parseInt(input.getAttribute("maxlength"), 10) || 100;

        var counter = parentDoc.createElement("div");

        counter.style.fontSize = "12px";
        counter.style.color = "#666";
        counter.style.textAlign = "right";
        counter.style.marginTop = "2px";

        var updateCounter = function () {
            var length = input.value ? input.value.length : 0;
            counter.textContent = length + "/" + maxLength;
        };

        if (input.parentNode) {
            input.parentNode.insertBefore(
                counter,
                input.nextSibling
            );
        }

        updateCounter();

        input.addEventListener("input", updateCounter);
    }, 500);
};

Let's look at the most important parts of the code.

Locating the input field

var dataId = "dps_title.fieldControl-text-box-text";

This variable identifies the HTML input element associated with the Title field.

The following selector searches for that element:

var input = parentDoc.querySelector(
    '[data-id="' + dataId + '"]'
);

The selector is specific to the rendered Dynamics 365 form and may need to be adjusted if the field's HTML structure changes.

Waiting for the field to become available

var interval = setInterval(function () {
    // Search for the input field
}, 500);

Dynamics 365 forms can render their controls asynchronously. The input may not be available immediately when the form's OnLoad event executes.

Using setInterval allows the script to check every 500 milliseconds until the field is found.

Once the input becomes available, clearInterval(interval) stops further checks.

Preventing duplicate initialization

if (input.dataset.charCounterInitialized === "true") {
    return;
}

input.dataset.charCounterInitialized = "true";

This check prevents the same input from being initialized more than once.

It is particularly useful when form events or custom scripts can be triggered repeatedly.

Reading the maximum length

var maxLength =
    parseInt(input.getAttribute("maxlength"), 10) || 100;

The script reads the maxlength attribute from the input element.

If the attribute is missing or its value cannot be parsed, the counter uses 100 as a fallback.

It is important to configure the fallback according to the actual maximum length of your field.

Creating the counter element

var counter = parentDoc.createElement("div");

counter.style.fontSize = "12px";
counter.style.color = "#666";
counter.style.textAlign = "right";
counter.style.marginTop = "2px";

A new HTML div is created to display the character count.

The styling keeps the counter small, discreet, and right-aligned beneath the field.

The counter is then inserted immediately after the input element:

input.parentNode.insertBefore(
    counter,
    input.nextSibling
);

Updating the counter dynamically

var updateCounter = function () {
    var length = input.value ? input.value.length : 0;
    counter.textContent = length + "/" + maxLength;
};

This function calculates the current length of the input value and displays it alongside the maximum allowed length.

The input event ensures that the counter updates when the user types, pastes text, or otherwise changes the input value.

input.addEventListener("input", updateCounter);

Finally, calling updateCounter() immediately initializes the counter with the current field value.

Step 2: Register the Function on the Form OnLoad Event

After defining the counter function, we need to call it when the Dynamics 365 form loads.

_self.OnLoad = async function (executionContext) {

    if (executionContext !== null) {
        _formContext = executionContext.getFormContext();
        _executionContext = executionContext;
    }

    utilities = DPS.utilities;
    utilities.load(_formContext);

    _self.InitTitleCharCounter();
};

In this example, the existing form initialization logic is preserved.

The execution context provides access to the current form context, while the existing DPS.utilities initialization remains unchanged.

The following line starts the character counter initialization:

_self.InitTitleCharCounter();

Make sure that the JavaScript web resource is registered with the appropriate form event handler and that the execution context is passed to the OnLoad function.

If your existing application uses a different initialization pattern, integrate the call into that pattern instead of replacing other required form logic.

Step 3: Expected Result

Once the form loads and the Title input becomes available, the counter appears directly below the field.

For example:

User actionCounter display
The field is empty0/100
The user enters 12 characters12/100
The user enters 47 characters47/100
The user reaches the limit100/100

The counter updates immediately as the user edits the field.

Note: The character counter is an informational UI enhancement. It does not replace Dynamics 365's built-in maximum-length validation or server-side validation.

Important Considerations

Although this approach can work in a specific Dynamics 365 environment, there are a few important considerations.

1. Accessing the parent document

The code uses:

var parentDoc = window.parent.document;

This assumes that the parent document contains the target input and that browser security restrictions allow access to it.

However, Dynamics 365 model-driven apps use a managed user interface, and accessing internal DOM elements is not a supported public form customization API. Such selectors and DOM manipulations may break after platform updates.

For a production implementation, test the solution carefully in your specific environment and consider a supported custom page or PCF control when a durable custom UI is required.

2. Keep the fallback consistent

If the actual field limit is 255 characters, change the fallback accordingly:

var maxLength =
    parseInt(input.getAttribute("maxlength"), 10) || 255;

The counter should reflect the actual configured limit, rather than an arbitrary value.

3. Consider field re-rendering

The implementation stops polling after finding the input. If Dynamics 365 later replaces the input element during a form refresh or UI re-render, the counter and event listener may no longer be attached to the active element.

A more robust implementation would account for the lifecycle of the control and avoid unnecessary repeated polling.

4. Consider Unicode characters

JavaScript's string.length counts UTF-16 code units, not necessarily user-perceived characters. Some emoji and other Unicode symbols can therefore count as two or more units.

For standard CRM text input, value.length is often sufficient, but the counting method should match the validation rules and expected behavior of the application.

Adding a character counter to a Dynamics 365 CRM text field is a simple way to improve usability and data entry.

By locating the input, creating a small counter element, and listening for the input event, we can provide users with immediate feedback about the number of characters they have entered.

The key benefits are improved visibility, fewer input errors, and a more user-friendly form experience.

For production solutions, however, it is important to consider the limitations of direct DOM manipulation and prefer supported customization options whenever possible.