Partially Mask Your Input Content with Vue
Tags: vue, javascript
Published: 2021-07-13 10:35:47

The requirement is to partial hide input content, for example ![masked input](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9d7m1zc7nitb5jucitks.png) The normal way of masking input content is using ``<input type="password">``, it utilizes the logic of input content masking provided by browser. However, it does not meet the requirement of show **partial masked content + rest unmasked content** without losing original unmasked content.

Searching the online resource, no articles have addressed this issue before. Majority of the articles are tutorials on how to do fully masked input content but not partially mask. I decided to come out my own implementation.

Building this component on top of vue, this is the way how it has been done. User cursor position is the first problem, it is necessary to find current cursor position for deletion or insertion value based on user action. How to find where cursor current stops. With ![selectionstart mdn](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4cg4bsz5z4lnkrfptdy.png), browser actually has provided api to achieve this. In order to track the user behavior on input, previous value and current value compared to determine whether it is deletion or insertion.
```javascript
const preInput = this.preInput;
const index = e.target.selectionStart;
const nowValue = e.target.value;
//compare length of previous current input value
const increment = nowValue.length - preInput.length;
```
If nowValue is longer, new value will be inserted to the position where it points to
```javascript
if (increment >= 0) {
 const newStr = nowValue.slice(index - increment, index);
 this.preInput = preInput.slice(0, index - increment) + newStr  
                 preVal.slice(index - increment)
} 
```
on the other hand, if some input are deleted
```javascript
else if (increment < 0) {
 this.preInput = preInput.slice(0, index) + 
                 preInput.slice(index - increment);
}
```
with knowing deletion or insertion and get updated value,
the next step is to render new value with predetermined masking rule to render new value on input, let say last 2 characters are need to be shown of string with rest string are masked with '*'
```javascript
if (nowValue.length > 0) {
 const len = nowValue.length-2 > 0 ? nowVal.length - 2 : 0;
 e.target.value = new Array(len).fill('*').join("") + 
                  nowValue.slice(len);
}
```
At here, masked value are created with the way of array fill in content with masked symbol, and update with input native property. The original value is stored at preInput. At the end of this circle, 
```javascript 
console.log("original content:", this.preInput)
```
retrieve unmasked original content.

For the whole view,

```javascript
//v-model needs to be indicate in order for input to receive value
<input v-model="hiddenInput" @input="onInput"/>

onInput(e){
 const preInput = this.preInput;
 const index = e.target.selectionStart;
 const nowValue = e.target.value;
 const increment = nowValue.length - preInput.length;

 if (increment >= 0) {
  const newStr = nowValue.slice(index - increment, index);
  this.preInput = preInput.slice(0, index - increment) + 
                  newStr + preVal.slice(index - increment)
 } else if (increment < 0) {
  this.preInput = preInput.slice(0, index) + 
                  preInput.slice(index - increment);
 }

 if (nowValue.length > 0) {
   //define the mask rule, here is to visualize last two digits
  const len = nowValue.length-2 > 0 ? nowVal.length - 2 : 0;
  e.target.value = new Array(len).fill('*').join("") + 
  nowValue.slice(len);
 }
  //update cursor
  e.target.setSelectionRange(index, index)
}
```

Thanks for https://github.com/s0urcelab/YouShallPass the inspiration