Skip to content

Custom Mask Directive

The v-mask directive use to mask input value.

Usage

v-mask will automatically format the input value based on the provided mask.

Select a mask from the dropdown

View Code
vue
<script setup>
import { ref, watch } from "vue";
const mask = ref("###");
const maskPatterns = [
  "###-###-####",
  "###.###.####",
  "### ### ####",
  "###",
  "@@@@@-###",
  "###-###-###",
  "###.###",
  "(###) ###-####",
];
const inp = ref("");
watch(mask, () => {
  inp.value = "";
});
</script>
<template>
  <div class="w-full p-5 bg-zinc-100 dark:bg-zinc-800 rounded-2xl space-y-3">
    <p>
      <code>v-mask</code> will automatically format the input value based on the
      provided mask.
    </p>
    <div>
      <p class="text-sm text-gray-500 dark:text-gray-400">
        Select a mask from the dropdown
      </p>
      <select
        class="w-full h-10 ring-[1px] ring-zinc-600 bg-zinc-50 dark:bg-zinc-700 resize-none outline-none rounded-xl p-2"
        v-model="mask"
      >
        <option v-for="pattern in maskPatterns" :key="pattern" :value="pattern">
          {{ pattern }}
        </option>
      </select>
    </div>
    <input
      v-custom-mask="mask"
      placeholder="Mask"
      v-model="inp"
      :style="[
        mask === 'hex' && inp.length === 7
          ? `border: 1px solid ${inp}`
          : 'border: 1px solid #4f46e5',
      ]"
      class="w-full h-10 bg-zinc-50 dark:bg-zinc-700 resize-none outline-none rounded-xl p-2"
    />
  </div>
</template>
<style scoped></style>

Arguments

The v-mask directive does not accept any arguments.

Modifiers

The v-mask directive does not accept any modifiers.

Source Code

js
const setMaxLength = (el, length) => {
  el.setAttribute("maxlength", length);
};

function applyMask(el, value) {
  let mask = el.getAttribute("mask-data");

  setMaxLength(el, mask.length);
  let maskedValue = "";
  let maskArr = mask.split("");
  let valueArr = value.split("");
  let maskedArr = [];

  for (let i = 0; i < maskArr.length; i++) {
    const currentMaskChar = maskArr[i];
    const currentValueChar = valueArr[i];
    if (currentValueChar === undefined) {
      break;
    } else if (currentMaskChar === "@") {
      if (/^[a-zA-Z]$/.test(currentValueChar)) {
        maskedArr.push(currentValueChar);
        // watch for the next mask char, if not @ or #, add it to the masked value
        if (i + 1 < maskArr.length) {
          const nextMaskChar = maskArr[i + 1];
          if (nextMaskChar !== "@" && nextMaskChar !== "#") {
            maskedArr.push(nextMaskChar);
            i++;
          }
        }
      }
    } else if (currentMaskChar === "#") {
      if (/^\d$/.test(currentValueChar)) {
        maskedArr.push(currentValueChar);
        // watch for the next mask char, if not @ or #, add it to the masked value
        if (i + 1 < maskArr.length) {
          const nextMaskChar = maskArr[i + 1];
          if (nextMaskChar !== "@" && nextMaskChar !== "#") {
            maskedArr.push(nextMaskChar);
            i++;
          }
        }
      }
    } else {
      maskedArr.push(currentMaskChar);
    }
  }
  maskedValue = maskedArr.join("");
  return maskedValue;
}

function custom(el, binding, event) {
  if (!el || !binding || !binding.value) {
    return;
  }

  const mask = binding.value;
  if (typeof mask !== "string") {
    console.error("Invalid mask format");
    return;
  }

  function applyMaskOnInput() {
    let value = el.value;

    // Check if there is an existing value before applying the mask
    if (value) {
      let maskedValue = applyMask(el, value, event);
      el.value = maskedValue;
    }
  }

  // Apply the mask on input
  el.addEventListener("input", applyMaskOnInput);
}
export const vCustomMask = {
  mounted(el, binding) {
    el.setAttribute("mask-data", binding.value);
    el.addEventListener("input", () => {
      // check the first value matches the mask, if not then remove it
      let maskVal = el.getAttribute("mask-data");
      let maskArr = maskVal.split("");
      let valueArr = el.value.split("");
      if (maskArr[0] === "@") {
        if (!/^[a-zA-Z]$/.test(valueArr[0])) {
          el.value = "";
        }
      } else if (maskArr[0] === "#") {
        if (!/^\d$/.test(valueArr[0])) {
          el.value = "";
        }
      }
      custom(el, binding);
    });
  },
  updated(el, binding) {
    if (binding.value !== binding.oldValue) {
      el.setAttribute("mask-data", binding.value);
      let mask = binding.value;
      if (mask[0] != "#" && mask[0] != "@") {
        el.value = mask[0];
      }
    }
    el.addEventListener("input", () => {
      custom(el, binding);
    });
  },
};