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:
Initialize the character counter when the CRM form loads.
Locate the title input element in the form's document.
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 action | Counter display |
|---|---|
| The field is empty | 0/100 |
| The user enters 12 characters | 12/100 |
| The user enters 47 characters | 47/100 |
| The user reaches the limit | 100/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.