Creating an input mask with Lit
I needed to create an input mask for a Lit project I am working on. After a little research, i choose IMask library. In this article, I’ll show you how to create an input mask in a Lit template using a directive.
I could create a custom element to wrap the input element and handle the mask logic, but using a directive is a more elegant and flexible solution.
First, naive version
It’s a simple directive that receives a mask string as an argument and applies it to the input element using the IMask library. The class overrides update
method so it can access the part element, checks if element or any of its children is an input and apply the mask to it. Since it does not render anything, it returns noChange
.
It should be used as a element part:
So far, so good. But there is a problem with this implementation. One of my requirements is to be able to be define the mask in a parent of the input element. Something like the below example:
Still, the directive will work as expected. But the actual usage is not exactly like that. I have an input
function that renders the input element:
This time, the directive will not work as expected. The problem is that at the time update
is called, the child input element is not yet rendered. So, querySelector("input")
will return null
. Check this Lit Playground to see the problem in action.
Second, improved version
Here is an improved version that uses a MutationObserver to wait for a input element to be added to the DOM:
This works as expected in all cases.
Conclusion
This article shows how to create an input mask in a Lit template using a directive. It highlights a difference in the timing of the children rendering when using nested templates and how to handle it using a MutationObserver.
The same technique can be used to create other directives that need to access child elements.