4

I am trying to write a custom Lightning Web Component that displays a single lightning-input text component and dynamically masks the input as the user enters it. By "masks" I mean it reformats the user's input as the user types.

For example, the user could type the 9 digits of a U.S. Social Security number and the mask would automatically insert the dashes in the correct places.

I have attempted to do this using third-party JavaScript libraries in two different ways:

  1. Uploaded as a static resource and loaded into the component via loadScript().

  2. Including the third-party library as its own Lightning Web Component and importing it.

In both cases I tried to apply the input mask to the input field inside the renderedCallback hook.

import { loadScript } from 'lightning/platformResourceLoader';
import vanillaTextMask from '@salesforce/resourceurl/vanillaTextMask';

async renderedCallback() {
  await loadScript(this, vanillaTextMask);
  const input = this.template.querySelector('lightning-input');
  vanillaTextMask.maskInput({
    inputElement: input,
    mask: [/\d/, /\d/, /\d/, '-', /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/]
  });
}
import maskInput from 'c/textMask';

async renderedCallback() {
  const input = this.template.querySelector('lightning-input');
  maskInput({
    inputElement: input,
    mask: [/\d/, /\d/, /\d/, '-', /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/]
  });
}

I've tried multiple libraries (text mask, Inputmask), different values for the loadScript arguments, different querySelector arguments, and a plain input element as opposed to a lightning-input. The best result I've had so far is getting placeholder characters displaying in the input field on focus, ___-__-____, but even in this case, the typing is hindered as the cursor position constantly jumps to the end of the input after each character is typed. The input is not practically usable.

I wonder if the fact that querySelector returns a Proxy object as opposed to the actual DOM element is causing the issue.

4
  • Why not provide 3 input texts of shorter width of type='password' and hypen in between them? Jul 23, 2019 at 20:22
  • Chris, did you ever figure this out? Oct 24, 2019 at 16:10
  • @JeremyA.West Unfortunately I concluded that, since Salesforce gives you a Proxy object back instead of the raw DOM element, it is currently not possible to do any sort of on-the-fly masking of an input value. I settled for a LWC that grabs the value after it has been typed, formats it appropriately, puts it back in the field, and runs validation. When the user focuses on the input field it removes the mask and lets them edit the "raw" value again.
    – Chris
    Nov 13, 2019 at 18:02
  • It's worth mentioning that lightning-input is missing certain features of the standard input, including the keydown event and the setSelectionRange function. Both are used in this commonly used masking solution: stackoverflow.com/a/55010378/7718025. That being said, if you're very determined, it is possible to roll your own custom input component and use LDS to style it. Jun 22 at 21:32

2 Answers 2

4

Unfortunately I concluded that, since Salesforce gives you a Proxy object back instead of the raw DOM element, it is currently not possible to do any sort of on-the-fly masking of an input value. I settled for a LWC that grabs the value after it has been typed, formats it appropriately, puts it back in the field, and runs validation. When the user focuses on the input field it removes the mask and lets them edit the "raw" value again:

HTML

<template>
    <lightning-input
        type="text"
        pattern="[0-9]{3}-[0-9]{2}-[0-9]{4}"
        onfocus={strip}
        onblur={mask}
        onchange={handleChange}
        value={value}
    ></lightning-input>
</template>

JavaScript

const applyMask = input => {
    let value = input.replace(/-/g, '');
    if (value.length >= 6) {
        value = `${value.slice(0, 3)}-${value.slice(3, 5)}-${value.slice(5)}`;
    } else if (value.length >= 4) {
        value = `${value.slice(0, 3)}-${value.slice(3)}`;
    }
    return value;
};

const removeMask = input => input.replace(/-/g, '');

export default class SsnInput extends LightningElement {

    /**
     * ...
     */
    @api value;

    connectedCallback() {
        if (this.value) {
            this.value = applyMask(this.value);
        }
    }

    strip(event) {
      event.target.value = removeMask(event.target.value);
    }

    mask(event) {
        event.target.value = applyMask(event.target.value);
        event.target.reportValidity();
    }

    handleChange(event) {
        ...
    }
}
2

Solution is documented here : https://www.mhamzas.com/blog/2020/05/21/adding-an-input-mask-to-a-lightning-web-component/

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .